Quand est eval evil en PHP?

84

Au cours de toutes les années que j'ai développées en php, j'ai toujours entendu dire que l'utilisation eval()était maléfique.

Compte tenu du code suivant, ne serait-il pas logique d'utiliser la deuxième option (et plus élégante)? Si non, pourquoi?

// $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).';');
Pierre Spring
la source
2
eval est TOUJOURS maléfique, il y a toujours une meilleure façon d'écrire du code, surtout depuis que PHP a introduit des fonctions anonymes. Dans ce cas, j'utiliserais$result = array(); preg_replace_callback('#^enum\s*\(\s*\'|\'\s*\)\s*$#', function($m) use($result) { $result[] = $m[1]; }, $type);
Geoffrey
Eh bien, honnêtement, je pense que le principal problème avec php n'est pas la langue elle-même mais les personnes qui l'utilisent. Les trois bonnes réponses à cette question (thomasrutter, braincracking et la mienne) ont toutes reçu des votes négatifs sans que personne n'ait un point contre eux. D'autre part, une réponse prétend que "Parfois, eval () est la seule / la bonne solution" sans exemple ni explication et obtient le plus grand vote pour cela ...
Francois Bourgeois

Réponses:

133

Je serais prudent en appelant eval () pur evil. L'évaluation dynamique est un outil puissant et peut parfois sauver la vie. Avec eval (), on peut contourner les lacunes de PHP (voir ci-dessous).

Les principaux problèmes avec eval () sont:

  • Entrée potentiellement dangereuse. Passer un paramètre non approuvé est un moyen d'échouer. Ce n'est souvent pas une tâche facile de s'assurer qu'un paramètre (ou une partie de celui-ci) est entièrement fiable.
  • Trickiness. Utiliser eval () rend le code intelligent, donc plus difficile à suivre. Pour citer Brian Kernighan "Le débogage est deux fois plus difficile que l'écriture du code en premier lieu. Par conséquent, si vous écrivez le code aussi intelligemment que possible, vous n'êtes, par définition, pas assez intelligent pour le déboguer "

Le principal problème avec l'utilisation réelle de eval () est un seul:

  • Des développeurs inexpérimentés qui l'utilisent sans suffisamment de considération.

En règle générale, j'ai tendance à suivre ceci:

  1. Parfois, eval () est la seule / la bonne solution.
  2. Dans la plupart des cas, il faut essayer autre chose.
  3. En cas de doute, passez à 2.
  4. Sinon, soyez très, très prudent.
Michał Rudnicki
la source
4
Cela pourrait être remanié pour éviter également eval (), surtout si $ className est sur la liste blanche, ce qu'il doit être pour pouvoir utiliser eval (). La bonne nouvelle est cependant qu'à partir de la 5.3, $ foo :: bar () est valide.
rojoca
@rojoca: Pouvez-vous nous donner un exemple comment faire sans eval () en PHP 5.2, s'il vous plaît?
Michał Rudnicki
30
Je ne sais pas si cela compte comme eval ou non, mais $ result = call_user_func (array ('Foo', 'bar')); fonctionne comme un charme.
Ionuț G. Stan
3
Bon point à propos de la difficulté - dans votre (simple) exemple, une variable apparaît "de nulle part". Si le code devient juste un peu plus complexe, bonne chance à la prochaine personne qui regarde le code et essaie de chasser cette variable (été là, fait ça, tout ce que j'ai eu était un mal de tête et ce t-shirt moche).
Piskvor a quitté le bâtiment
L'entrée dangereuse est évitable
thepowerlies
40

eval est mauvais quand il n'y a que la moindre possibilité que userinput soit inclus dans la chaîne évaluée. Lorsque vous évaluez sans contenu provenant d'un utilisateur, vous devez être en sécurité.

Néanmoins, vous devriez réfléchir au moins deux fois avant d'utiliser eval, cela a l'air d'une simplicité trompeuse, mais avec la gestion des erreurs (voir le commentaire de VBAssassins), la débuggabilité, etc. à l'esprit, ce n'est plus si simple.

Donc, en règle générale: oubliez ça. Lorsque eval est la réponse, vous posez probablement la mauvaise question! ;-)

Patrick Cornelissen
la source
6
Parfois, eval EST la réponse. Nous travaillons sur des applications de jeux en ligne et il est très difficile d'éviter les évaluations là-bas en raison des relations très complexes entre les entités ... mais à mon humble avis, dans 90% des cas, eval n'est pas la réponse.
Jet le
19
Je serais vraiment curieux de voir une situation où SEULE eval est la réponse.
Ionuț G. Stan
2
@Ionut G. Stan Déclencheurs personnalisés stockés dans la base de données pour les objets / entités?
Kuroki Kaze le
8
Je n'achète pas vraiment que l'une de ces utilisations soit justifiée de eval (). Dans chaque cas, faire la même chose sans utiliser eval () est toujours au moins possible. Ce n'est pas comme si PHP était paralysé sans eval (). Certes, eval () est un raccourci dans des cas comme ceux-ci, mais cela rend toujours votre chemin de code un peu plus difficile à suivre et les problèmes un peu plus difficiles à déboguer. C'est mon avis.
thomasrutter
4
@Christian: VOUS devriez d'abord écrire un exemple (utilisez pastebin.com , et collez le lien ici), où vous pensez qu'il eval()est impossible d' éviter l'utilisation de , c'est une meilleure approche. Beaucoup de gens disent que eval()c'est inévitable dans certains cas, mais ils ne mentionnent aucun exemple spécifique que nous pourrions argumenter - de cette façon, ce débat n'a pas de sens. Alors s'il vous plaît, les gens qui disent que eval()c'est inévitable, prouvez-le d'abord!
Sk8erPeter
18

eval () est également mauvais à tout moment.

"Quand eval () n'est-il pas mauvais?" est la mauvaise question à poser à mon avis, car elle semble impliquer que les inconvénients de l'utilisation de eval () disparaissent comme par magie dans certains contextes.

Utiliser eval () est généralement une mauvaise idée car cela diminue la lisibilité du code, la possibilité pour vous de prédire le chemin du code (et les implications de sécurité possibles de cela) avant l'exécution, et donc la capacité de déboguer le code. Utiliser eval () peut également empêcher le code évalué et le code qui l'entoure d'être optimisé par un cache d'opcode tel que le Zend Opcache intégré à PHP 5.5 et supérieur, ou par un compilateur JIT tel que celui de HHVM.

De plus, il n'y a aucune situation pour laquelle il est absolument nécessaire d'utiliser eval () - PHP est un langage de programmation pleinement capable sans lui.

Que vous les voyiez ou non comme des maux ou que vous puissiez justifier personnellement l'utilisation de eval () dans certains cas, cela dépend de vous. Pour certains, les maux sont trop grands pour jamais le justifier, et pour d'autres, eval () est un raccourci pratique.

Cependant, si vous voyez eval () comme un mal, c'est le mal à tout moment. Il ne perd pas comme par magie sa perversité selon le contexte.

thomasrutter
la source
1
Vous faites un sacré programmeur.
Christian
1
«De plus, il n'y a pas de situation pour laquelle il est absolument nécessaire d'utiliser eval ()» - Et si je veux exécuter du code que j'ai stocké dans une base de données, selon l'exemple donné dans la documentation PHP?
Yarin
4
À cela, je dirais que stocker du code PHP dans la base de données est tout aussi mauvais, et également inutile. Quoi que vous souhaitiez réaliser, il existe d'autres moyens de le faire que de stocker PHP dans la base de données ou d'utiliser eval (). Faites-le si vous le souhaitez, et si c'est un raccourci utile pour vous, mais si vous traitez eval () comme un mal, vous devrez probablement traiter le stockage de PHP dans la base de données comme un mal aussi.
thomasrutter
12
Il ajoute un autre vecteur d'attaque - une injection SQL pourrait alors également exécuter du code PHP arbitraire sur le serveur Web. Il nécessite l'utilisation de eval (). Il ne peut pas être optimisé par les caches de bytecode. Cela viole le principe de séparation de votre code et de vos données. Cela peut rendre votre application moins portable - pour mettre à jour / réparer le PHP, vous devez également mettre à jour la base de données.
thomasrutter
3
Je dirais que si quelque chose ne peut être réalisé qu'en utilisant eval, il serait peut-être bon de reconsidérer le fait de le faire. La création dynamique de classes au moment de l'exécution semble être une mauvaise idée. Pourquoi ne pas utiliser la génération de code et générer le code qui les définit à l'avance?
thomasrutter
15

Dans ce cas, eval est probablement suffisamment sûr, tant qu'il n'est jamais possible de créer des colonnes arbitraires dans une table par un utilisateur.

Ce n'est pas vraiment plus élégant cependant. Il s'agit essentiellement d'un problème d'analyse de texte, et abuser de l'analyseur PHP pour le gérer semble un peu piraté. Si vous voulez abuser des fonctionnalités du langage, pourquoi ne pas abuser de l'analyseur JSON? Au moins avec l'analyseur JSON, il n'y a aucune possibilité d'injection de code.

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

Une expression régulière est probablement le moyen le plus évident. Vous pouvez utiliser une seule expression régulière pour extraire toutes les valeurs de cette chaîne:

$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];
BlackAura
la source
1
même si cela ne répond pas à la question en soi, c'est une très bonne réponse à la question: quelle serait la meilleure façon d'analyser une énumération ()… merci;)
Pierre Spring
13

eval() est lent, mais je n'appellerais pas ça mal.

C'est le mauvais usage que nous en faisons qui peut conduire à l' injection de code et être mauvais.

Un exemple simple:

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

Un exemple néfaste:

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

Je vous conseillerais de ne pas utiliser, eval()mais si vous le faites, assurez-vous de valider / mettre en liste blanche toutes les entrées.

Alix Axel
la source
12

Lorsque vous utilisez des données étrangères (telles que les entrées utilisateur) dans eval.

Dans votre exemple ci-dessus, ce n'est pas un problème.

GreenieMeanie
la source
7

Je vais voler de manière flagrante le contenu ici:

  1. L'évaluation, par sa nature, sera toujours un problème de sécurité.

  2. Outre les problèmes de sécurité, eval a également le problème d'être incroyablement lent. Dans mes tests sur PHP 4.3.10, son code est 10 fois plus lent que le code normal et 28 fois plus lent sur PHP 5.1 beta1.

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

stefs
la source
5

eval()est toujours mauvais.

  • pour des raisons de sécurité
  • pour des raisons de performances
  • pour des raisons de lisibilité / réutilisabilité
  • pour des raisons IDE / outil
  • pour des raisons de débogage
  • il y a toujours un meilleur moyen
François Bourgeois
la source
@bracketworks: Mais vous vous trompez - tous ces problèmes inhérents ne disparaissent pas comme par magie dans certaines situations. Pourquoi devraient-ils? Prenons l'exemple de la performance: Pensez-vous vraiment que l'interpréteur exécutera la fonction extrêmement lente eval () avec une vitesse accrue uniquement parce que vous l'avez utilisée d'une manière mystique "pas mal"? Ou ce débogage étape par étape fonctionnera dans votre clause d'évaluation un jour de chance?
Francois Bourgeois
3
Franchement, je pense que la réponse de @ MichałRudnicki le dit le mieux. Ne vous méprenez pas, chaque fois que je me pose une question, et la réponse est eval(), je suppose juste que j'ai posé la mauvaise question; C'est rarement la «bonne» réponse, mais dire que c'est toujours le mal est tout simplement incorrect.
Dan Lugg
si je veux stocker le code dans la base de données? comment puis-je l'exécuter?
Konstantin XFlash Stratigenas
@KonstantinXFlashStratigenas Vous devriez vous demander pourquoi vous stockez du code exécutable dans une base de données. Une base de données est pour les données résultant de votre code - pas votre code. À tout le moins, vous augmentez les vecteurs d'attaque.
Jack B
4

Je prêterais également attention aux personnes qui maintiennent votre code.

eval () n'est pas facile à regarder et à savoir ce qui est censé se passer, votre exemple n'est pas si mauvais, mais dans d'autres endroits, cela peut être un vrai cauchemar.


la source
4

Personnellement, je pense que ce code est encore assez mauvais parce que vous ne commentez pas ce qu'il fait. Il ne teste pas non plus la validité de ses entrées, ce qui le rend très fragile.

Je pense également que, puisque 95% (ou plus) des utilisations d'Eval sont activement dangereuses, le petit gain de temps potentiel qu'il pourrait fournir dans d'autres cas ne vaut pas la peine de se livrer à la mauvaise pratique de l'utiliser. De plus, vous devrez plus tard expliquer à vos serviteurs pourquoi votre utilisation d'Eval est bonne et la leur mauvaise.

Et, bien sûr, votre PHP finit par ressembler à Perl;)

Il y a deux problèmes clés avec eval (), (comme un scénario "d'attaque par injection"):

1) Cela peut causer des dommages 2) Il peut simplement s'écraser

et un qui est plus social que technique:

3) Cela incitera les gens à l'utiliser de manière inappropriée comme raccourci ailleurs

Dans le premier cas, vous courez le risque (évidemment, pas lorsque vous évaluez une chaîne connue) de l'exécution de code arbitraire. Cependant, vos entrées peuvent ne pas être aussi connues ou aussi fixes que vous le pensez.

Plus probablement (dans ce cas) vous ne ferez que planter, et votre chaîne se terminera par un message d'erreur gratuit et obscur. À mon humble avis, tout le code doit échouer aussi proprement que possible, faute de quoi il doit lancer une exception (comme la forme d'erreur la plus gérable).

Je suggérerais que, dans cet exemple, vous codez par coïncidence plutôt que par comportement. Oui, l'instruction SQL enum (et êtes-vous sûr que l'énumération de ce champ? - avez-vous appelé le bon champ de la bonne table de la bonne version de la base de données? mais je suggérerais que ce que vous voulez vraiment faire n'est pas de trouver le chemin le plus court de l'entrée à la sortie, mais plutôt de vous attaquer à la tâche spécifiée:

  • Identifiez que vous avez une énumération
  • Extraire la liste intérieure
  • Décompressez les valeurs de la liste

Ce qui est à peu près ce que fait votre option, mais j'envelopperais quelques if et des commentaires pour plus de clarté et de sécurité (par exemple, si la première correspondance ne correspond pas, lancez une exception ou définissez un résultat nul).

Il y a encore des problèmes possibles avec des virgules ou des guillemets, et vous devriez probablement décompresser les données puis les retirer, mais cela traite au moins les données comme des données, plutôt que comme du code.

Avec la version preg_version, votre pire résultat sera probablement $ result = null, avec la version eval, le pire est inconnu, mais au moins un crash.

Phase d'analyse
la source
3

eval évalue une chaîne en tant que code, le problème avec cela est que si la chaîne est en quelque sorte «corrompue», elle peut exposer d'énormes menaces de sécurité. Normalement, le problème est dans un cas où l'entrée de l'utilisateur est évaluée dans la chaîne dans de nombreux cas, l'utilisateur pourrait entrer du code (php ou ssi par exemple) qui est ensuite exécuté dans eval, il s'exécuterait avec les mêmes autorisations que votre script php et pourrait être utilisé pour obtenir des informations / accéder à votre serveur. Il peut être assez difficile de s'assurer que l'entrée utilisateur est correctement nettoyée avant de la transmettre à eval. Il y a d'autres problèmes ... dont certains sont discutables

Toby
la source
3

PHP vous conseille d'écrire votre code de telle manière qu'il puisse être exécuté via call_user_func au lieu de faire des évaluations explicites.

Nolte
la source
2

Une autre raison evalest le mal, c'est qu'il ne pouvait pas être mis en cache par des caches de bytecode PHP comme eAccelertor ou ACP.

TheHippo
la source
2

C'est une mauvaise programmation qui rend eval () mauvais, pas la fonction. Je l'utilise parfois, car je n'arrive pas à le contourner en programmation dynamique sur plusieurs sites. Je ne peux pas faire analyser PHP sur un site, car je ne recevrai pas les choses que je veux. Je recevrais juste un résultat! Je suis heureux qu'une fonction comme eval () existe, car cela me facilite la vie. Entrée utilisateur? Seuls les mauvais programmeurs sont accrochés par des pirates. Je ne m'inquiète pas pour ça.

Igor M. - PortalPress.org
la source
2

C'est une mauvaise programmation qui rend eval () mauvais, pas la fonction. Je l'utilise parfois, car je n'arrive pas à le contourner en programmation dynamique sur plusieurs sites. Je ne peux pas faire analyser PHP sur un site, car je ne recevrai pas les choses que je veux. Je recevrais juste un résultat! Je suis heureux qu'une fonction comme eval () existe, car cela me facilite la vie. Entrée utilisateur? Seuls les mauvais programmeurs sont accrochés par des pirates. Je ne m'inquiète pas pour ça.

Je prédis que vous aurez bientôt de sérieux problèmes ...

En toute honnêteté, il n'y a absolument aucune bonne utilité pour une fonction exorbitante comme eval, dans un langage interprété tel que PHP. Je n'ai jamais vu eval exécuter des fonctions de programme qui n'auraient pas pu être exécutées par d'autres moyens plus sûrs ...

Eval est la racine de tout mal, je suis tout à fait d'accord, pour toutes les personnes qui pensent que tester les entrées des utilisateurs aidera. Réfléchissez à deux fois, les entrées des utilisateurs peuvent prendre de nombreuses formes différentes, et au moment où nous parlons, les pirates exploitent cette fonction dont vous ne vous souciez pas assez. À mon avis, évitez tout simplement eval.

J'ai vu des exemples conçus pour abuser de la fonction d'évaluation qui surpassait ma propre créativité. Du point de vue de la sécurité, évitez à tout prix, et j'irais même jusqu'à exiger qu'il soit à tout le moins une option dans la configuration PHP, plutôt qu'un «donné».

Casse-tête
la source
Le raisonnement selon lequel "peu importe avec quel soin vous essayez de sécuriser votre code concernant la fonctionnalité X, les hackers intelligents ont toujours une longueur d'avance ..." pourrait s'appliquer à toute autre technique, pas seulement X == eval. Et par conséquent, pour toute fonctionnalité X: X est la racine de tous les eval ... euh ... mal, alors mieux vaut abandonner complètement la programmation (tirant ainsi le tapis de ces hackers stupides, les déjouant encore finalement).
Sz.
"il n'y a absolument aucun bon usage pour une fonction exorbitante comme eval" -> quiconque peut utiliser des mots comme 'absolument', est soit nouveau en programmation, soit un mauvais programmeur.
unity100
2

Voici une solution pour exécuter du code PHP extrait d'une base de données sans utiliser eval. Permet toutes les fonctions et exceptions de la portée:

$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');

Fondamentalement, il crée une fonction unique avec le code inclus dans un fichier texte, inclut le fichier, appelle la fonction, puis supprime le fichier une fois terminé. J'utilise ceci pour effectuer des ingestions / synchronisations quotidiennes de bases de données où chaque étape nécessite un code unique à traiter. Cela a résolu tous les problèmes auxquels je faisais face.

Jason
la source
Cela ressemble à une réponse à une réponse; veuillez l'afficher comme tel lorsque vous le pouvez.
rfornal le
1

J'utilisais beaucoup eval (), mais j'ai trouvé dans la plupart des cas que vous n'avez pas besoin d'utiliser eval pour faire des tours. Eh bien, vous avez call_user_func () et call_user_func_array () en PHP. Il suffit d'appeler statiquement et dynamiquement n'importe quelle méthode.

Pour effectuer un appel statique, construisez votre rappel sous forme de tableau ('nom_classe', 'nom_méthode'), ou même sous forme de chaîne simple comme 'nom_classe :: nom_méthode'. Pour effectuer un appel dynamique, utilisez le rappel de style array ($ object, 'method').

La seule utilisation judicieuse de eval () est d'écrire un compilateur personnalisé. J'en ai fait un, mais eval est toujours mauvais, car il est tellement difficile à déboguer. Le pire est que l'erreur fatale dans le code évalué bloque le code qui l'a appelé. J'ai utilisé l'extension Parsekit PECL pour vérifier au moins la syntaxe, mais toujours pas de joie - essayez de faire référence à une classe inconnue et à des plantages d'application entiers.

Harry
la source
0

Hormis les problèmes de sécurité, eval () ne peut pas être compilé, optimisé ou mis en cache opcode, donc il sera toujours plus lent - bien plus lent - que le code PHP normal. Il n'est donc pas performant d'utiliser eval, même si cela ne le rend pas mauvais. ( gotoc'est mal, evalc'est seulement une mauvaise pratique / code malodorant / moche)

Aron Cederholm
la source
evalobtient une cote perverse inférieure à goto? Est-ce le jour opposé?
JLRishe
C'est juste que j'en veux aux développeurs php pour avoir réellement implémenté gotoen 5.3.
Aron Cederholm le
1
Non, aux goto-fobiens: il y a les outils, et il y a les artisans, qui les utilisent (ou pas). Ce ne sont jamais les outils qui font passer un professionnel comme un idiot, quand il fait des erreurs, mais plutôt l'incapacité d'utiliser les bons outils (correctement). Mais ce sont toujours les outils à blâmer ...
Sz.
0

La plupart des gens souligneront le fait que cela peut être dangereux lorsque vous traitez avec les entrées de l'utilisateur (ce qui est possible).

Pour moi, le pire est que cela réduit la maintenabilité de votre code:

  • Difficile à déboguer
  • Difficile à mettre à jour
  • Limite l'utilisation des outils et des assistants (comme les IDE)
les powerlies
la source