->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> Exploitation des vulnérabilités avec les applications PHP <
< Inspiré du Document original de Shaun Clowes SecureReality >
> Traduction par tobozo@users.sourceforge.net <
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
--- < 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 :
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 (), 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 :
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 :
\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 :
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 :
et construire l'url en forcant la declaration de $libdir avec la valeur
"http:///", lors de l'execution de l'include, le script va aller
chercher le fichier langages.php sur le serveur au lieu d'aller le
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 :
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 :
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):
Dans libdir/loadlangage.php:
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 :
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 :
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_'. Ce fichier contient
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' et repertoire /tmp.
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