Upload des images et ses dangers

mieux protéger une application internet

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
La société actuelle mets l'image sur le piédestal. L'Internet l'aide dans cette démarche. Facebook, Flickr et même GoogleMaps attirent grâce au visuel. Cependant, les services avec la possibilité du transfert des images doivent faire très attention car il y a beaucoup de moyens d'attaque.

Dans le développement Web il n'y a jamais une méthode infaillible. Cependant à travers cet article vous verrez qu'il y a un moyen assez efficace pour se protéger contre les attaques concernant les transferts des fichiers images. Des images peu communes car celles qui possèdent le code PHP inséré dans le contenu.

Inquiété par l’alerte donnée il y a 3 ans par ha.ckers.org (http://ha.ckers.org/blog/20070604/passing-malicious-php-through-getimagesize/), j'ai décidé d'analyser d'abord si la fonction getimagesize() est dangereuse. Ensuite j'aborderai deux méthodes qui pourront renforcer la sécurité de l'application web. A la fin je présenterai la méthode qui enlèvera le code PHP inséré dans l'image.

1. Getimagesize()
La fonction getimagesize() ne peut pas être le seul moyen de vérification du format du fichier transféré. Elle peut déterminer si une photo est horizontale ou verticale mais, par exemple, elle ne peut pas détecter les commentaires inclus dans le fichier.

Pour les tests j'ai employé le fichier crocus.gif. Dedans j'ai placé le commentaire suivant:



Ensuite j'ai passé aux tests. Au début j'ai voulu voir si la fonction getimagesize() peut elle-même mettre notre application en difficulté. Résultat est négatif et le code suivant affiche seulement les informations sur l'image:



Le problème est arrivé plus tard, quand je voulais inclure cette image avec require_once(). Cette fonction a provoqué la lecture des commentaires et l'affichage des informations de ma configuration sur l'écran:

Mais il y a peu de développeurs qui inclussent une image de telle manière. Méthode la plus courante d'utilisation des images est un simple . Et là l'image avec le code PHP injecté s'affiche normalement:


<img src="crocus.gif" alt="" />


On s'aperçoit bien que getimagesize() n'est pas dangereuse en elle-même. Cependant, elle n'est peut pas être la seule barrière de protection.

2. Le renommage
Visuellement, on aime mieux l'image nommée velo.jpg plutôt que 29320.jpg. Cependant, il faut faire attention au nommage des fichiers. Imaginez-vous la situation suivante:
On possède un répertoire img. Dedans on place des images transférées par les utilisateurs. Le premier pas à ouvrir la porte à l'attaquant est une mauvaise vérification du type du fichier transféré. On suppose que l'attaquant est arrivé à envoyer le fichier .htaccess avec le contenu suivant:

AddType application/x-httpd-php .gif


Cette instruction va exécuter chaque fichier .gif en tant que script PHP. Maintenant en essayant d'ouvrir notre crocus.gif, on ne voit pas la représentation de cette fleur mais le résultat du commentaire inclus dans ce fichier.



L'absence de ce fichier va afficher l'image normalement. Vous pouvez le constater sur les screens au-dessous:


Comment remédier à cette difficulté ? Soit on peut renommer chaque fichier, soit on peut vérifier si le fichier transféré s'appelle .htaccess.

3. Le soutien de .htaccess
Afin de sécuriser plus notre application, il ne faut pas avoir peur de faire recours au fichiers .htaccess. Comment l'utiliser dans le cas du transfert des fichiers sur le serveur ?

Quand on n'est pas sûr si l’application fournie pour le transfert des fichiers ne filtre pas bien les extensions, on peut mettre dans le répertoire un fichier .htaccess avec le contenu suivant:

AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi
Options -ExecCGI


Ce code va bloquer l'accès aux fichiers avec les extensions listées à partir d'un navigateur. Cependant, il ne bloquera pas l'exécution de ces fichiers au cas d'une inclusion:

require_once 'upload/php.php';


Ceci provoquera l'exécution du script php.php avec le contenu:
echo phpinfo();


4. Création d'une nouvelle image
Et si l'on pouvait enlever les commentaires en créant un nouveau fichiers avec la fonction createimagefromgif ?

Regardons ce qui se passe quand on exécute le code suivant:

$img = imagecreatefromgif(''.$_SERVER['DOCUMENT_ROOT'].'/upload/c.gif');
$width = imagesx($img);
$height = imagesy($img);
$img_mini = imagecreatetruecolor($width, $height);
imagecopyresampled($img_mini, $img, 0, 0, 0, 0, $width , $height, $width , $height);
imagejpeg($img_mini, ''.$_SERVER['DOCUMENT_ROOT'].'/upload/alfabet.jpg', 100);


Le résultat ? Une image valable, sans le code php dedans. A-t-on réussi ? Pourtant le type d'image returné est .jpg et non pas .gif. Essayons de faire la même chose avec imagegif:


$img = imagecreatefromgif(''.$_SERVER['DOCUMENT_ROOT'].'/upload/c.gif');
$width = imagesx($img);
$height = imagesy($img);
$img_mini = imagecreatetruecolor($width, $height);
imagecopyresampled($img_mini, $img, 0, 0, 0, 0, $width , $height, $width , $height);
imagegif($img_mini, ''.$_SERVER['DOCUMENT_ROOT'].'/upload/alfabet.gif', 100);


Qu'est-ce qu'on voit ? La même chose que dans la situation du fichier .jpg. L'image ne contient pas le code PHP. Regardons le fragment vulnérable:


require_once 'alfabet.jpg';
require_once 'alfabet.gif';


Les deux inclusions n'affichent pas les informations sur la configuration de notre PHP.



Comme vous pouvez le constater une nouvelle fois, il ne faut pas faire une confiance aveugle à des données passées par l'internaute. Comme dans le cas de données textuelles, ici aussi il faut les filtrer pour ne pas mettre l'application en danger.

Pour en savoir plus:
http://www.scanit.be/uploads/php-file-upload.pdf
http://ha.ckers.org/blog/20070604/passing-malicious-php-through-getimagesize/

L'article écrit en rythme de:
Slaï - Comme une flamme
Bartosz KONIECZNY 10-06-2010 11:05 sécurité des applications web
Un conseil PHP

Fonctions dynamiques.

PHP permet de construire des fonctions à partir des variables. On peut très simplement construire une méthode dynamique qui génère une image en fonction du type de fichier transféré :

// filename has only alphanumerical caracters
$names = explode('.', $_FILES['image1']['name']);
$fname = "imagecreatefrom".$names[1];
$fname('fileOne');