web-dev-qa-db-fra.com

Quand est-ce que Eval est mal en PHP?

Pendant toutes les années que j'ai développées en php, j'ai toujours entendu dire que l'utilisation de eval() est mauvaise.

Compte tenu du code suivant, ne serait-il pas judicieux d'utiliser la deuxième option (et plus élégante)? Sinon, 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).';');
82
Pierre Spring

Je serais prudent en appelant eval () pure evil. L'évaluation dynamique est un outil puissant et peut parfois être une bouée de sauvetage. 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. Il n'est souvent pas facile de s'assurer qu'un paramètre (ou une partie de celui-ci) est entièrement fiable.
  • Trickiness. L'utilisation de eval () rend le code intelligent, donc plus difficile à suivre. Pour citer Brian Kernighan " Le débogage est deux fois plus difficile que d'écrire le 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 () n'en est qu'un:

  • 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.
128
Michał Rudnicki

eval est mauvais lorsqu'il n'y a que la moindre possibilité que l'entrée utilisateur soit incluse dans la chaîne évaluée. Lorsque vous effectuez une évaluation 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, il semble d'une simplicité trompeuse, mais avec la gestion des erreurs (voir le commentaire VBAssassins), le débogage, 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! ;-)

39

eval () est également mauvais en tout temps.

"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.

L'utilisation de eval () est généralement une mauvaise idée car elle diminue la lisibilité du code, la possibilité pour vous de prédire le chemin du code (et les éventuelles implications de sécurité de celui-ci) avant l'exécution, et donc la possibilité de déboguer le code. L'utilisation d'eval () peut également empêcher le code évalué et le code qui l'entoure d'être optimisé par un cache d'opcode tel que 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 entièrement capable sans lui.

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

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

17
thomasrutter

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 hacky. Si vous souhaitez 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];
14
BlackAura

Lorsque vous utilisez des données étrangères (telles que des entrées utilisateur) à l'intérieur de l'évaluation.

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

11
GreenieMeanie

eval() est lent, mais je ne dirais pas que c'est mal.

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

Un exemple simple:

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

Un exemple dangereux:

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

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

10
Alix Axel

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

  1. Eval, de 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 10 fois plus lent que normal et 28 fois plus lent sur PHP 5.1 beta1.

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

7
stefs

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 principaux avec eval (), (comme un scénario "d'attaque par injection"):

1) Cela peut causer du tort 2) Il peut simplement s'écraser

et celui qui est plus social que technique:

3) Cela tentera les gens de 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. Vos entrées peuvent ne pas être aussi connues ou fixes que vous le pensez.

Il est plus probable (dans ce cas) que vous plantiez, et votre chaîne se terminera avec un message d'erreur gratuit et obscur. À mon humble avis, tout le code devrait échouer aussi soigneusement que possible, faute de quoi il devrait lever une exception (comme la forme d'erreur la plus maniable).

Je suggère que, dans cet exemple, vous codiez par coïncidence plutôt que de coder en fonction du comportement. Oui, l'instruction SQL enum (et êtes-vous sûr que l'énumération du champ? - avez-vous appelé le bon champ de la bonne table de la bonne version de la base de données? A-t-elle réellement répondu?) Ressemble à la syntaxe de déclaration de tableau en PHP, mais je suggère que ce que vous voulez vraiment faire, ce 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

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

Il y a encore quelques problèmes possibles avec les virgules ou les guillemets échappés, et vous devriez probablement décompresser les données puis les supprimer, 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 est probablement $ result = null, avec la version eval, le pire est inconnu, mais au moins un plantage.

5
Parsingphase

Je ferais également attention aux personnes qui gèrent votre code.

eval () n'est pas la facilité pour simplement regarder et savoir ce qui est censé se produire, votre exemple n'est pas si mauvais, mais dans d'autres endroits, cela peut être un bon cauchemar.

4
user83632

eval() est toujours mauvais.

  • pour des raisons de sécurité
  • pour des raisons de performances
  • pour des raisons de lisibilité/réutilisabilité
  • pour IDE/raisons de l'outil
  • pour des raisons de débogage
  • il y a toujours une meilleure façon
4
Francois Bourgeois

eval évalue une chaîne en tant que code, le problème est que si la chaîne est en quelque sorte "entachée", elle pourrait exposer d'énormes menaces de sécurité. Normalement, le problème se situe dans le cas où l'entrée utilisateur est évaluée dans la chaîne dans de nombreux cas, l'utilisateur peut 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ès à votre serveur. Il peut être assez difficile de s'assurer que l'entrée utilisateur est correctement nettoyée avant de la remettre à eval. Il y a d'autres problèmes ... dont certains sont discutables

3
Toby

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.

3
Nolte

C'est une mauvaise programmation qui rend mal eval (), pas la fonction. Je l'utilise parfois, car je ne peux pas le contourner en programmation dynamique sur plusieurs sites. Je ne peux pas avoir PHP étant analysé sur un site, car je ne recevrai pas les choses que je veux. Je recevrais juste un résultat! Je suis content qu'une fonction comme eval () existe, car cela me rend la vie beaucoup plus facile. Saisie par l'utilisateur? Seuls les mauvais programmeurs sont connectés par des pirates. Je ne m'en fais pas.

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

En toute honnêteté, il n'y a absolument aucune bonne utilisation d'une fonction exorbitante telle que 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 en utilisant d'autres moyens plus sûrs ...

Eval est la racine de tous les maux, je le reconnais de tout cœur, pour toutes les personnes qui pensent que tester les entrées des utilisateurs aidera. Réfléchissez-y à deux fois, les commentaires des utilisateurs peuvent prendre de nombreuses formes différentes et, en ce moment même, les pirates informatiques exploitent cette fonction qui ne vous intéressait pas assez. À mon avis, évitez tout simplement l'eval.

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

2
Braincracking

C'est une mauvaise programmation qui rend mal eval (), pas la fonction. Je l'utilise parfois, car je ne peux pas le contourner en programmation dynamique sur plusieurs sites. Je ne peux pas avoir PHP étant analysé sur un site, car je ne recevrai pas les choses que je veux. Je recevrais juste un résultat! Je suis content qu'une fonction comme eval () existe, car cela me rend la vie beaucoup plus facile. Saisie par l'utilisateur? Seuls les mauvais programmeurs sont connectés par des pirates. Je ne m'en fais pas.

2

Une autre raison pour laquelle eval est mauvais est qu'il n'a pas pu être mis en cache par PHP caches de bytecode comme eAccelertor ou ACP.

2
TheHippo

J'utilisais beaucoup eval (), mais j'ai trouvé la plupart des cas où 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. C'est assez bon pour 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 tableau ($ object, 'method').

La seule utilisation judicieuse pour eval () est d'écrire un compilateur personnalisé. J'en ai fait un, mais eval est toujours diabolique, car il est tellement difficile à déboguer. Le pire est l'erreur fatale dans le code évalué qui 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.

1
Harry

Voici une solution pour exécuter PHP extrait d'une base de données sans utiliser eval. Permet toutes les fonctions et exceptions de 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é. Je l'utilise pour effectuer des ingestions/synchronisations quotidiennes de bases de données où chaque étape nécessite un code unique à gérer pour être traité. Cela a résolu tous les problèmes auxquels j'étais confronté.

1
Jason

Outre les problèmes de sécurité, eval () ne peut pas être compilé, optimisé ou mis en cache d'opcode, il sera donc toujours plus lent - beaucoup 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. (goto est mauvais, eval n'est que mauvaise pratique/code malodorant/laid)

0
Aron Cederholm