Top Code 2024, les challenges sont de nouveau disponibles dans les boards pour les participant(e)s => Boards

eval est-elle vraiment evil ? Comment bien utiliser cette fonction en PHP

Comprendre la fonction eval en PHP : son utilité et ses risques.

→ Corrigé du Challenge : Braquage du coffre #1

eval en PHP

Cette solution au challenge Braquage du coffre #1 a été proposé par Kim. Il a eu la bonne idée d’utiliser la fonction « eval » disponible en PHP qui permet d’exécuter du code PHP stocké dans une chaine de caractères et donc écrit par le programme lui même. Cela peut être utile pour des tâches comme la génération de code à la volée ou le traitement de données dynamiques. Mais c’est surtout une fonction souvent décriée voir « interdite d’utilisation » car elle présente un grand danger… D’où le jeu de mots « eval is evil… »

Nous allons parcourir le code puis expliquer comment eval peut être (ou non) utilisée dans tes programmes.

Le corrigé

$code = $depart;
foreach ($chemin as $item) {
    $code = $code . $item[0] . pow(10, (strlen($item) - 1));
}

$reponse = eval('return ' . $code . ';');

Un peu d’explications :

  • Pour un jeu de données du challenge, $code va contenir quelque chose comme : 8107-1000-1000-1000000+100+100-100-10-10-10+1+1
  • Le $code commence donc avec la valeur de départ
  • Pour chaque $item, on prend le premier caractère avec $item[0] qui servira donc d’opérateur, + ou -.
  • On utilise ensuite la notion de puissance, qui permet de passer du nombre de caractères à une puissance de 10. Avec l’astuce mathématique que 10° est égal à 1.
  • Enfin, la fonction « eval » va executer ce code, la valeur sera récupérée grâce au return dans $reponse.

On aurait aussi pu faire quelque chose comme ça :

eval('$reponse = '.$code.';');

Pourquoi la fonction eval en PHP est dangereuse ?

Il est important de comprendre que la fonction eval doit être utilisée avec une extrême prudence.

L’évaluation de code dynamique peut ouvrir la porte à de nombreuses vulnérabilités, notamment les injections de code malveillant. Si tu ne contrôles pas strictement ce qui est passé à eval, tu risques de créer des failles de sécurité importantes dans ton application. En effet, eval va exécuter le code fourni sans se poser de question. Si ce code stipule que tu dois afficher les identifiants de la base de données, ou exécuter une commande via la fonction exec, eval va le faire ! Il faut donc réserver l’utilisation d’eval à des cas où la chaine de caractères qui sera évaluée est scrupuleusement contrôlée et maitrisée de A à Z. Il faut par exemple éviter d’utiliser eval avec des données saisies par l’utilisateur.

Comment brider le code passé dans la fonction eval ?

Il existe des méthodes permettant de contrôler le code qui sera exécuté, par exemple en retirant les fonctions que l’on ne souhaiterait pas voir exécuter.

Un exemple avec la méthode preg_replace :

$code = '...'; // Le code à évaluer
$restrictedFunctions = ['exec', 'passthru', 'shell_exec'];
foreach ($restrictedFunctions as $func) {
    $code = preg_replace("/\b$func\b/", "", $code);
}
eval($code);

En effet, les fonctions exec ou shell_exec permettent d’executer des scripts… Il ne vaut mieux pas que n’importe qui puisse déclencher n’importe quoi…

Malheureusement, ces méthodes ne sont pas 100% efficaces, on peut jouer avec de la concaténation et des variables variables pour que le preg_replace ne détecte pas exactement le nom de la chaine, par exemple :

$fn = 'exe' . 'c';
$cmd = 'rm -rf /config'; // Efface tout le contenu du dossier /config
$fn($cmd); // Et voilà...

J’ai tenté plusieurs choses mais je n’ai pas trouvé de moyen de détecter que ce code allait appeler la fonction « exec »… Si jamais tu connais une méthode, n’hésite pas à nous écrire !

Bonnes pratiques pour l’utilisation de eval

  • Il existe des façons de contrôler ou nettoyer en amont ce que eval va exécuter comme code mais ces techniques ne sont malheureusement pas 100% efficaces.
  • Si la chaine de caractères à exécuter est construite avec des informations en provenance de l’utilisateur, il ne faut pas utiliser eval et trouver une façon différente de réaliser l’opération.
  • eval ne doit pas être utilisée par facilité, pour éviter une complexité algorithmique ou pour « aller + vite » dans le code.
  • Il y a des cas où eval se prête bien : traitement de données dynamiques, calculateur complexe (avec équations, etc.)

Tu peux aller tester eval directement dans notre sandbox PHP. L’exécution du code de la sandbox est déléguée à un service tiers qui gère très bien sa sécurité, pas la peine d’essayer des choses bizarres 😉

Et merci à Kim pour le partage de cette solution !


Qui a codé ce superbe contenu ?

Keep learning

Autres contenus à découvrir


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.