Execution de code PHP sécurisée

Eléphanteau du PHP | 13 Messages

07 juin 2007, 17:21

Bonjour,

je souhaiterai permettre à mes visiteurs d'exécuter du code php directement depuis les pages de mon site (un formulaire avec le code, puis exécution dans un eval() sur la page suivante).
Toutes les fonctions ne sont pas nécessaires, il faudrait simplement qu'ils aient acces :
- aux variables, tableaux
- création de fonctions et utilisation de celles-ci
- structure de controles (if, for, foreach, etc...)

Cependant si j'exécute le code tel qu'il est envoyé il est à peu près certain que des petits malins feront appel à d'autres fonctions pour pirater le site par exemple.

Donc voilà, j'aurai voulu savoir quelles précautions vous prendriez.
Merci :)

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9782 Messages

07 juin 2007, 19:00

Bonjour,

A première vu je ne vois pas de solution simple...
Je me permet toutefois de souligner que ce que tu veux faire est très très risqué d'un point de vue de la sécurité, je te déconseille donc fortement l'utilisation de eval() avec du code que tu ne maîtrises pas ou que tu maîtrises mal.

/me n'aimerait pas être ton hébergeur...
Quand tout le reste a échoué, lisez le mode d'emploi...

Eléphant du PHP | 199 Messages

07 juin 2007, 19:02

Je rejoins l'avis d'@rthur.
Klomac - Blog Lambda

Eléphanteau du PHP | 13 Messages

07 juin 2007, 19:12

Oui c'est sur qu'il faut être très prudent. C'est aussi un peu pour ça que je demande votre avis ^^

Je viens de découvrir la fonction token_get_all, je vais regarder un peu et je vous dirai quoi :)

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

07 juin 2007, 19:43

Ouaip, c'est ce que j'allais proposer. Tu peux essayer une solution basée sur le tokenizer, mais il va te falloir gérer quelles fonctions tu veux autoriser et quelles fonctions tu veux interdire donc c'est pas gagné. Sans oublier la possibilité de ralentir ton serveur avec une boucle infinie comme
while(1){md5(time());}
Sans oublier tous les fopen(), etc... qui pourraient avoir de grosses conséquences.

ViPHP
ViPHP | 5924 Messages

07 juin 2007, 22:09

Ouaip, c'est ce que j'allais proposer. Tu peux essayer une solution basée sur le tokenizer, mais il va te falloir gérer quelles fonctions tu veux autoriser et quelles fonctions tu veux interdire donc c'est pas gagné. Sans oublier la possibilité de ralentir ton serveur avec une boucle infinie comme
while(1){md5(time());}
Sans oublier tous les fopen(), etc... qui pourraient avoir de grosses conséquences.
Il faudrait limite un analyseur derrière pour détecter les actes de malveillance ou de maladresse (le while(1) est un bon exemple) avant d'exécuter le code...

Eléphanteau du PHP | 13 Messages

08 juin 2007, 00:44

Merci pour votre aide :merci:
Il faudrait limite un analyseur derrière pour détecter les actes de malveillance ou de maladresse (le while(1) est un bon exemple) avant d'exécuter le code...
Tu pourrai préciser un peu ? :)


Sinon pour le reste:
A priori il est possible de détecter facilement tous les éléments dangereux (includes, fonctions, classes, etc) avec le découpage en tokkens de l'analyseur, ou au contraire de faire une liste avec les éléments autorisés.

Par contre il reste un problème au niveau des variables.
- Tout d'abord, il est possible d'accéder aux variables de la page. Je ne peux pas me contenter d'analyser le contenu des T_VARIABLE (c'est à dire le nom des variables) à cause des variables variables.
- Du coup, il est possible que le code se réécrive sur lui-même. Du genre ... $this->code = "readfile('config.php'); ... " au milieu du code. Surtout que dans mon cas le code doit être executé plusieurs fois, donc dès la seconde fois le nouveau code sera exécuté.


Voila... si vous avez une idée pour remédier à ce problème, elle est la bienvenue :) Peut-etre qu'empecher les variables variables résoudrait le problème ?

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

08 juin 2007, 00:55

Quand Sékiltoyai parle d'analyseur, c'est pas un truc incorporé à PHP que tu aurais raté en feuilletant le manuel, désolé :lol:

En fait ça correspond à ce dont tu parles, une routine "maison" qui efface tout ce qui parait suspect. L'embêtant c'est qu'il y a toujours un moyen de contourner ce genre de protections donc si quelqu'un veut nuire au site et est prêt à y passer un petit peu de temps, il y parviendra.

Quelques exemples de trucs qu'un utilisateur malveillant pourrait essayer sur ton système
$x = 'eval';
$x($code_malveillant);
call_user_func(create_function('', 'eval(\'exec("rm -rf /tout/ton/disque")\')'));
ob_start('eval');echo "exec('rm -rf /oh/no');";
preg_replace('#.*#e', "eval('\\1');", "exec('rm -rf /oh/no');");
Je n'en ai testé aucun, mais ça te donne une idée du genre de trucs tordus qu'on peut essayer pour passer outre toutes sortes de protections.

ViPHP
ViPHP | 5924 Messages

08 juin 2007, 01:14

Oui, mais à ce moment là ce sera à lui de ne laisser que les syntaxes de base. De toute facon, si Idaho doit recoder entièrement l'interpréteur PHP, il va en avoir pour longtemps...

Eléphanteau du PHP | 13 Messages

08 juin 2007, 01:17

Il pourrait essayer mais ça ne marcherai pas :p
Les fonctions peuvent etre toutes vérifiées, leur nom figure dans T_STRING, il suffit de le comparer à une liste de fonctions autorisées. Par contre il faut effectivement penser à interdire aussi create_function (et juste garder le mot function), je n'y avai pas pensé. Enfin cela rend ces trois là inutilisables :
call_user_func(create_function('', 'eval(\'exec("rm -rf /tout/ton/disque")\')'));
ob_start('eval');echo "exec('rm -rf /oh/no');";
Au passage je n'ai pas bien compris celui-ci, à aucun moment il n'est executé.. :
preg_replace('#.*#e', "eval('\\1');", "exec('rm -rf /oh/no');");


Par contre pour le dernier je m'avoue vaincu pour le moment :
$x = 'eval';
$x($code_malveillant);

Je devrait peut être faire l'inverse, et vérifier que les lignes correspondent à des masques, en imposant une action par ligne... mais c'est moins drôle.

ViPHP
ViPHP | 5924 Messages

08 juin 2007, 01:19

Il faudrait que tu fasses une liste des fonctions autorisées, si tu autorises l'utilisation de certaines fonctions non critiques (is_int ou abs...).

Eléphanteau du PHP | 13 Messages

08 juin 2007, 02:14

Oui certaines fonctions inoffensives (enfin je crois) seront accessibles (sur les chaines de caractères et mathématiques), rien d'autre.
Donc pour les fonctions appelées explicitement, pas de souci.

* Ensuite pour les fonctions appelées de manière fourbe :
$x = 'eval';
$x($evilcode);
Il suffit de vérifier qu'un nom de variable ne précède pas le symbole '('.

* Enfin pour les variables variables, du genre :
${'secr'.$i}
Il faut simplement s'assurer que le token '$' n'est pas présent (correspondant au $ du début, ${...) dans le code. En effet pour les 'vraies' variables ('$' inclus) le token T_VARIABLE est utilisé.


Il me semble que ça résout les problèmes cités précédemment. Enfin si je n'ai rien oublié...

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

08 juin 2007, 02:36

je n'y avai pas pensé.
On ne peut pas penser à tout, tu vas invariablement oublier des trucs et quelques qui a du temps devant lui pourra les exploiter, c'est pour ça que je disais que ce genre de page est voué à l'échec. À la rigueur, regarde du côté de "runkit", il était possible de créer des "sandbox" et désactiver des fonctions à l'interieur de ces sandboxes, ce serait plus fiable qu'un validateur, mais je ne suis pas sûr que runkit soit maintenu à jour.
Au passage je n'ai pas bien compris celui-ci, à aucun moment il n'est executé..
Il doit falloir remplacer \\1 par \\0, ce n'est qu'un exemple approximatif, pour te donner une idée du principe (modifieur "e" de preg_replace()).

Enfin, bonne chance quand même.

ViPHP
ViPHP | 5924 Messages

08 juin 2007, 02:48

Idaho, tu comptes toujours faire exécuter le code par PHP ?
Tu ne trouves pas que serait mieux de l'exécuter toi même, c'est à dire implémenter un petit langage tout con proche de php ?

Eléphanteau du PHP | 13 Messages

08 juin 2007, 10:13

En ce qui concerne runkit, je n'ai malheureusement pas la possibilité d'installer des extensions :(

C'est vrai qu'il y a toujours le risque d'oublier quelque chose. De toute façon tout autoriser par défaut et bloquer certaines choses, ça n'a jamais été une méthode fiable... Donc je vais plutôt m'orienter vers la création d'un petit interpréteur, avec une syntaxe identique à celle de PHP.

Voila, merci :)