Quando è eval evil in php?


84

In tutti gli anni che ho sviluppato in php, ho sempre sentito dire che usare eval()è male.

Considerando il codice seguente, non avrebbe senso utilizzare la seconda (e più elegante) opzione? In caso contrario, perché?

// $type is the result of an SQL statement
// e.g. SHOW COLUMNS FROM a_table LIKE 'a_column';
// hence you can be pretty sure about the consistency
// of your string
$type = "enum('a','b','c')";

// possibility one
$type_1 = preg_replace('#^enum\s*\(\s*\'|\'\s*\)\s*$#', '', $type);
$result = preg_split('#\'\s*,\s*\'#', $type_1);

// possibility two
eval('$result = '.preg_replace('#^enum#','array', $type).';');

2
eval è SEMPRE malvagio, c'è sempre un modo migliore per scrivere codice, specialmente da quando PHP ha introdotto funzioni anonime. In questo caso$result = array(); preg_replace_callback('#^enum\s*\(\s*\'|\'\s*\)\s*$#', function($m) use($result) { $result[] = $m[1]; }, $type);
userei

Bene, onestamente, penso che il problema principale con php non sia il linguaggio in sé ma le persone che lo usano. Le tre risposte corrette a questa domanda (thomasrutter, braincracking's e mia) hanno tutte ottenuto voti negativi senza che nessuno abbia un punto contro di loro. D'altra parte una risposta afferma che "A volte eval () è l'unica / la soluzione giusta" senza esempio o spiegazione e ottiene il voto più alto per questo ...
Francois Bourgeois

Risposte:


134

Sarei cauto nel chiamare eval () pure evil. La valutazione dinamica è uno strumento potente e talvolta può essere un salvavita. Con eval () si possono aggirare le carenze di PHP (vedi sotto).

I problemi principali con eval () sono:

  • Potenziale input non sicuro. Il passaggio di un parametro non attendibile è un modo per fallire. Spesso non è un compito banale assicurarsi che un parametro (o parte di esso) sia completamente attendibile.
  • Ingannevolezza. L'uso di eval () rende il codice intelligente, quindi più difficile da seguire. Per citare Brian Kernighan "Il debug è due volte più difficile che scrivere il codice in primo luogo. Pertanto, se scrivi il codice nel modo più intelligente possibile, per definizione non sei abbastanza intelligente da eseguirne il debug "

Il problema principale con l'uso effettivo di eval () è solo uno:

  • Sviluppatori inesperti che lo usano senza sufficiente considerazione.

Come regola generale, tendo a seguire questo:

  1. A volte eval () è l'unica / la soluzione giusta.
  2. Nella maggior parte dei casi si dovrebbe provare qualcos'altro.
  3. In caso di dubbi, vai a 2.
  4. Altrimenti, stai molto, molto attento.

4
Questo potrebbe essere refactored per evitare anche eval (), specialmente se $ className è nella whitelist, cosa che deve essere per intrattenere l'uso di eval (). La buona notizia è che, a partire dalla versione 5.3, $ foo :: bar () è valido.
rojoca

@rojoca: puoi farci un esempio su come farlo senza eval () in PHP 5.2, per favore?
Michał Rudnicki,

30
Non sono sicuro che conti come eval o meno, ma $ result = call_user_func (array ('Foo', 'bar')); funziona come un fascino.
Ionuț G. Stan,

3
Buon punto sulla difficoltà: nel tuo (semplice) esempio, una variabile viene fuori "dal nulla". Se il codice diventa un po 'più complesso, buona fortuna alla prossima persona che guarda il codice e cerca di inseguire quella variabile (ci sono stato, l'ho fatto, tutto quello che ho ottenuto è stato un mal di testa e questa t-shirt schifosa).
Piskvor ha lasciato l'edificio il

L'input non sicuro è evitabile
thepowerlies il

40

eval è male quando c'è solo la minima possibilità che userinput sia incluso nella stringa valutata. Quando esegui una valutazione senza contenuti che provengono da un utente, dovresti essere al sicuro.

Tuttavia dovresti pensarci almeno due volte prima di usare eval, sembra ingannevolmente semplice, ma con la gestione degli errori (vedi il commento di VBAssassins), il debuggabilità ecc. In mente, non è più così semplice.

Quindi, come regola generale: lascia perdere. Quando eval è la risposta, probabilmente stai facendo la domanda sbagliata! ;-)


6
A volte eval è la risposta. Lavoriamo su applicazioni di gioco online ed è molto difficile evitare le valutazioni lì a causa delle relazioni molto complesse tra le entità ... ma IMHO nel 90% dei casi eval non è la risposta.
Jet il

19
Sarei davvero curioso di vedere una situazione in cui SOLO eval è la risposta.
Ionuț G. Stan

2
@Ionut G. Stan Trigger personalizzati memorizzati nel database per oggetti / entità?
Kuroki Kaze

8
Non credo davvero che nessuno di questi siano usi giustificati di eval (). In ogni caso, fare la stessa cosa senza usare eval () è ancora almeno possibile. Non è che PHP sia paralizzato senza eval (). Certo, eval () è una scorciatoia in casi come questi, ma rende comunque il percorso del codice un po 'più difficile da seguire e problemi un po' più difficili da eseguire il debug. È la mia opinione.
thomasrutter

4
@Christian: TU dovresti scrivere prima un esempio (usa pastebin.com e incolla il link qui), dove pensi che evitare l'uso di eval()sia impossibile, questo è un approccio migliore. Molte persone dicono che eval()in alcuni casi è inevitabile, ma non menzionano esempi specifici che potremmo sostenere - in questo modo questo dibattito non ha senso. Quindi, per favore, le persone che dicono che eval()è inevitabile, provatelo prima!
Sk8erPeter

19

eval è ugualmente "malvagio" in ogni momento.

Se vedi eval () come malvagio, è sempre malvagio. Non perde magicamente la sua malvagità a seconda del contesto.

A mio parere, chiedendo "quando eval () non è male?" sembra implicare che gli svantaggi dell'uso di eval () scompaiano magicamente in alcuni contesti.

L'uso di eval () è generalmente una cattiva idea perché diminuisce la leggibilità del codice, la capacità di prevedere il percorso del codice (e le possibili implicazioni di sicurezza di questo) prima del runtime e quindi compromette la capacità di analizzare ed eseguire il debug del codice. L'uso di eval () può anche impedire che il codice valutato e il codice che lo circonda venga ottimizzato da una cache del codice operativo come Zend Opcache integrato in PHP 5.5 e versioni successive, o da un compilatore JIT come quello in HHVM.

Inoltre, non ci sono situazioni per le quali è assolutamente necessario usare eval () - PHP è un linguaggio di programmazione completamente funzionante senza di esso. Qualunque cosa tu voglia usare eval (), questo è un altro modo per farlo.

Se in realtà li vedi o meno come mali o puoi giustificare personalmente usando eval () in alcuni casi dipende da te. Per alcuni, i mali sono troppo grandi per giustificarlo, e per altri, eval () è una comoda scorciatoia.


2
Sei un bravissimo programmatore.
Christian

1
"Inoltre, non c'è nessuna situazione per cui è assolutamente necessario usare eval ()" - E se volessi eseguire il codice che ho memorizzato in un database, per l'esempio fornito nei documenti PHP?
Yarin

4
A questo direi che memorizzare il codice PHP nel database è ugualmente malvagio e altrettanto inutile. Qualunque cosa tu voglia ottenere, ci sono altri modi per farlo oltre a memorizzare PHP nel database o usare eval (). Fallo se vuoi, e se è una scorciatoia utile per te, ma se tratti eval () come un male, allora probabilmente dovresti considerare anche l'archiviazione di PHP nel database come un male.
thomasrutter

13
Aggiunge un altro vettore di attacco: un'iniezione SQL potrebbe quindi eseguire anche codice PHP arbitrario sul server web. Richiede l'uso di eval (). Non può essere ottimizzato dalle cache bytecode. Viola il principio di separazione del codice e dei dati. Può rendere la tua applicazione meno portabile: per aggiornare / correggere il PHP devi anche aggiornare il database.
thomasrutter

3
Direi che se qualcosa può essere ottenuto solo attraverso l'uso di eval, potrebbe essere bene riconsiderare di fare quel qualcosa. La creazione dinamica di classi in fase di esecuzione sembra una cattiva idea. Perché non impiegare la generazione di codice e generare il codice che li definisce in anticipo?
thomasrutter

15

In questo caso, eval è probabilmente abbastanza sicuro, a patto che non sia mai possibile che un utente crei colonne arbitrarie in una tabella.

Tuttavia, non è davvero più elegante. Questo è fondamentalmente un problema di analisi del testo e abusare del parser di PHP per gestirlo sembra un po 'hacker. Se vuoi abusare delle funzionalità della lingua, perché non abusare del parser JSON? Almeno con il parser JSON, non c'è alcuna possibilità di iniezione di codice.

$json = str_replace(array(
    'enum', '(', ')', "'"), array)
    '',     '[', ']', "'"), $type);
$result = json_decode($json);

Un'espressione regolare è probabilmente il modo più ovvio. Puoi utilizzare una singola espressione regolare per estrarre tutti i valori da questa stringa:

$extract_regex = '/
    (?<=,|enum\()   # Match strings that follow either a comma, or the string "enum("...
    \'      # ...then the opening quote mark...
    (.*?)       # ...and capture anything...
    \'      # ...up to the closing quote mark...
    /x';
preg_match_all($extract_regex, $type, $matches);
$result = $matches[1];

1
anche se questo non ha risposto alla domanda di per sé, questa è un'ottima risposta alla domanda: quale sarebbe il modo migliore per analizzare un enum ()… grazie;)
Pierre Spring,

13

eval() è lento, ma non lo chiamerei cattivo.

È il cattivo uso che ne facciamo che può portare all'iniezione di codice ed essere malvagio.

Un semplice esempio:

$_GET = 'echo 5 + 5 * 2;';
eval($_GET); // 15

Un esempio doloroso:

$_GET = 'system("reboot");';
eval($_GET); // oops

Ti consiglio di non usarlo, eval()ma se lo fai, assicurati di convalidare / inserire nella whitelist tutti gli input.


12

Quando si utilizzano dati esterni (come l'input dell'utente) all'interno di eval.

Nel tuo esempio sopra, questo non è un problema.


7

ruberò palesemente il contenuto qui:

  1. Eval per sua natura sarà sempre un problema di sicurezza.

  2. Oltre ai problemi di sicurezza, eval ha anche il problema di essere incredibilmente lento. Nei miei test su PHP 4.3.10 è 10 volte più lento del codice normale e 28 volte più lento su PHP 5.1 beta1.

blog.joshuaeichorn.com: using-eval-in-php


5

eval()è sempre il male.

  • per motivi di sicurezza
  • per motivi di prestazioni
  • per motivi di leggibilità / riusabilità
  • per motivi IDE / strumento
  • per motivi di debug
  • c'è sempre un modo migliore

@bracketworks: Ma ti sbagli: tutti questi problemi intrinseci non scompaiono magicamente in alcune situazioni. Perché dovrebbero? Prendiamo ad esempio le prestazioni: pensi davvero che l'interprete eseguirà la funzione estremamente lenta eval () con maggiore velocità solo perché l'hai usata in qualche modo mistico "non malvagio"? O quel debugging passo passo funzionerà all'interno della tua clausola eval in qualche giorno fortunato?
Francois Bourgeois

3
Francamente, penso che la risposta di @ MichałRudnicki lo dica meglio. Non fraintendetemi, ogni volta che mi pongo una domanda, e la risposta è eval()che presumerei appena di aver fatto la domanda sbagliata; raramente è la risposta "giusta", ma affermare che è sempre male è semplicemente sbagliato.
Dan Lugg

se voglio memorizzare il codice nel database? come posso eseguirlo?
Konstantin XFlash Stratigenas

@KonstantinXFlashStratigenas Dovresti chiederti perché stai memorizzando il codice eseguibile in un database. Un database è per i dati risultanti dal tuo codice, non dal tuo codice. Per lo meno, stai aumentando i vettori di attacco.
Jack B

4

Pagherei anche una certa considerazione alle persone che gestiscono il tuo codice.

eval () non è il modo più semplice per guardare e sapere cosa dovrebbe accadere, il tuo esempio non è poi così male, ma in altri posti può essere un vero incubo.


4

Personalmente, penso che il codice sia ancora piuttosto malvagio perché non stai commentando quello che sta facendo. Inoltre, non sta testando la validità dei suoi input, il che lo rende molto fragile.

Ritengo inoltre che, poiché il 95% (o più) degli usi di eval sono attivamente pericolosi, il piccolo potenziale risparmio di tempo che potrebbe fornire in altri casi non vale la pena di indulgere nella cattiva pratica di usarlo. Inoltre, in seguito dovrai spiegare ai tuoi servi perché il tuo uso di eval è buono e il loro cattivo.

E, naturalmente, il tuo PHP finisce per assomigliare a Perl;)

Ci sono due problemi chiave con eval (), (come uno scenario di "attacco injection"):

1) Potrebbe causare danni 2) Potrebbe semplicemente bloccarsi

e uno che è più sociale che tecnico:

3) Tenterà le persone a usarlo in modo inappropriato come scorciatoia altrove

Nel primo caso, corri il rischio (ovviamente, non quando stai valutando una stringa nota) di eseguire codice arbitrario. Tuttavia, i tuoi input potrebbero non essere così noti o fissi come pensi.

Più probabilmente (in questo caso) andrai in crash e la tua stringa terminerà con un messaggio di errore gratuito e oscuro. IMHO, tutto il codice dovrebbe fallire il più ordinatamente possibile, in caso contrario dovrebbe generare un'eccezione (come la forma di errore più gestibile).

Suggerirei che, in questo esempio, stai codificando per coincidenza piuttosto che codificare in base al comportamento. Sì, l'istruzione SQL enum (e sei sicuro che il campo sia enum? - hai chiamato il campo giusto della tabella giusta della versione corretta del database? Ha effettivamente risposto?) Sembra sintassi della dichiarazione di array in PHP, ma suggerirei che quello che vuoi veramente fare è non trovare il percorso più breve dall'input all'output, ma piuttosto affrontare l'attività specificata:

  • Identifica di avere un'enumerazione
  • Estrai l'elenco interno
  • Decomprimi i valori dell'elenco

Che è più o meno quello che fa la tua opzione, ma racchiuderei alcuni se e commenti attorno ad esso per chiarezza e sicurezza (ad esempio, se la prima corrispondenza non corrisponde, genera un'eccezione o imposta un risultato nullo).

Ci sono ancora alcuni possibili problemi con le virgole o le virgolette di escape, e dovresti probabilmente decomprimere i dati e poi cancellarli, ma almeno tratta i dati come dati, piuttosto che come codice.

Con preg_version è probabile che il risultato peggiore sia $ result = null, con la versione eval il peggiore è sconosciuto, ma almeno un crash.


3

eval valuta una stringa come codice, il problema è che se la stringa è in qualche modo "contaminata" potrebbe esporre enormi minacce alla sicurezza. Normalmente il problema è nel caso in cui l'input dell'utente viene valutato nella stringa in molti casi l'utente potrebbe inserire codice (php o ssi ad esempio) che viene quindi eseguito all'interno di eval, verrebbe eseguito con le stesse autorizzazioni del tuo script php e potrebbe essere utilizzato per ottenere informazioni / accesso al tuo server. Può essere abbastanza complicato assicurarsi che l'input dell'utente sia adeguatamente ripulito prima di consegnarlo a eval. Ci sono altri problemi ... alcuni dei quali discutibili


3

PHP consiglia di scrivere il codice in modo tale che possa essere eseguito tramite call_user_func invece di eseguire valutazioni esplicite.


2

Un altro motivo evalè il male è che non può essere memorizzato nella cache da cache bytecode PHP come eAccelertor o ACP.


2

È una cattiva programmazione che rende malvagia eval (), non la funzione. Lo uso a volte, poiché non riesco a aggirarlo nella programmazione dinamica su più siti. Non posso fare in modo che PHP venga analizzato in un sito, poiché non riceverò le cose che desidero. Vorrei solo ricevere un risultato! Sono felice che esista una funzione come eval (), poiché rende la mia vita molto più facile. Input dell'utente? Solo i cattivi programmatori vengono agganciati dagli hacker. Non mi preoccupo di questo.


2

È una cattiva programmazione che rende malvagia eval (), non la funzione. Lo uso a volte, poiché non riesco a aggirarlo nella programmazione dinamica su più siti. Non posso fare in modo che PHP venga analizzato in un sito, poiché non riceverò le cose che desidero. Vorrei solo ricevere un risultato! Sono felice che esista una funzione come eval (), poiché rende la mia vita molto più facile. Input dell'utente? Solo i cattivi programmatori vengono agganciati dagli hacker. Non mi preoccupo di questo.

Prevedo che presto avrai seri problemi ...

In tutta onestà, non c'è assolutamente alcun buon uso per una funzione esorbitante come eval, in un linguaggio interpretato come PHP. Non ho mai visto eval eseguire funzioni del programma che non avrebbero potuto essere eseguite utilizzando altri modi più sicuri ...

Eval è la radice di tutti i mali, sono d'accordo con tutto il cuore, per tutte le persone che pensano che testare l'input degli utenti aiuterà. Pensaci due volte, l'input dell'utente può avere molte forme diverse e mentre parliamo gli hacker stanno sfruttando quella funzione che non ti interessava abbastanza. Secondo me, eval del tutto eval.

Ho visto esempi realizzati per abusare della funzione di valutazione che ha superato la mia creatività. Dal punto di vista della sicurezza, evitatelo a tutti i costi, e mi spingerei addirittura a chiedere che sia come minimo un'opzione nella configurazione PHP, piuttosto che un "dato".


Il ragionamento di "non importa quanto attentamente si cerchi di proteggere il codice per quanto riguarda la funzione X, gli hacker intelligenti sono sempre un passo avanti ..." potrebbe essere applicato a qualsiasi altra tecnica, non solo a X == eval. E quindi, per qualsiasi caratteristica X: X è la radice di tutti gli eval ... ehm ... malvagi, quindi è meglio abbandonare del tutto la programmazione (togliendo così il tappeto da sotto quegli stupidi hacker, continuando a sconfiggerli alla fine).
Sz.

"non c'è assolutamente alcun buon uso per una funzione esorbitante come eval" -> chiunque possa usare parole come 'assolutamente', o è nuovo alla programmazione o è un cattivo programmatore.
100

2

Ecco una soluzione per eseguire codice PHP estratto da un database senza utilizzare eval. Consente tutte le funzioni e le eccezioni nell'ambito:

$rowId=1;  //database row id
$code="echo 'hello'; echo '\nThis is a test\n'; echo date(\"Y-m-d\");"; //php code pulled from database

$func="func{$rowId}";

file_put_contents('/tmp/tempFunction.php',"<?php\nfunction $func() {\n global \$rowId;\n$code\n}\n".chr(63).">");

include '/tmp/tempFunction.php';
call_user_func($func);
unlink ('/tmp/tempFunction.php');

Fondamentalmente crea una funzione unica con il codice incluso in un file di testo, include il file, chiama la funzione, quindi rimuove il file quando ha finito con esso. Lo sto usando per eseguire ingestioni / sincronizzazioni di database quotidiane in cui ogni passaggio richiede un codice univoco da gestire per l'elaborazione. Questo ha risolto tutti i problemi che stavo affrontando.


Sembra una risposta a una risposta; per favore pubblicalo come tale quando puoi.
rfornal

1

Usavo molto eval (), ma ho riscontrato che nella maggior parte dei casi non è necessario utilizzare eval per eseguire trucchi. Bene, hai call_user_func () e call_user_func_array () in PHP. È abbastanza buono per chiamare staticamente e dinamicamente qualsiasi metodo.

Per eseguire una chiamata statica, costruisci il tuo callback come array ('class_name', 'method_name'), o anche come semplice stringa come 'class_name :: method_name'. Per eseguire una chiamata dinamica utilizzare la richiamata in stile array ($ object, 'method').

L'unico uso sensato di eval () è scrivere un compilatore personalizzato. Ne ho fatto uno, ma eval è ancora malvagio, perché è così dannatamente difficile eseguire il debug. La cosa peggiore è che l'errore fatale nel codice valutato blocca il codice che lo ha chiamato. Ho usato l'estensione Parsekit PECL per controllare almeno la sintassi, ma ancora nessuna gioia: prova a fare riferimento alla classe sconosciuta e all'intera app si arresta.


0

A parte i problemi di sicurezza, eval () non può essere compilato, ottimizzato o memorizzato nella cache opcode, quindi sarà sempre più lento - molto più lento - del normale codice php. È quindi non performante usare eval, anche se questo non lo rende malvagio. ( gotoè cattivo, evalè solo una cattiva pratica / codice puzzolente / brutto)


evalottiene una valutazione del male inferiore a goto? È il giorno opposto?
JLRishe

È solo che nutro rancore nei confronti degli sviluppatori php per l'effettiva implementazione gotoin 5.3.
Aron Cederholm

1
Nah, a goto-fobians: ci sono gli strumenti, e ci sono gli artigiani, che li usano (o no). Non sono mai gli strumenti che fanno sembrare un idiota un professionista quando commette errori, ma piuttosto l'incapacità di usare gli strumenti giusti (correttamente). Ma sono sempre gli strumenti da incolpare ...
Sz.

0

La maggior parte delle persone sottolineerà il fatto che può essere pericoloso quando si ha a che fare con l'input dell'utente (che è possibile gestire).

Per me la parte peggiore è che riduce la manutenibilità del tuo codice:

  • Difficile eseguire il debug
  • Difficile da aggiornare
  • Limita l'utilizzo di strumenti e helper (come IDE)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.