L’upload de fichiers non sécurisé
Photo de profil, justificatif, pièce jointe, document à convertir : de nombreuses applications permettent d’envoyer un fichier. C’est l’une des fonctions les plus utiles et les plus délicates, car elle consiste à accepter un contenu entièrement contrôlé par un inconnu et à le stocker sur le serveur. C’est comme accepter un colis de n’importe qui et le ranger dans l’entrepôt sans rien vérifier : l’intérieur peut être inoffensif… ou pas.
Rappel légal
Déposer un fichier sur un serveur sans y être autorisé est une modification de système de données (articles 323-1 à 323-3-1 du Code pénal). Cette leçon décrit les risques et les contrôles à vérifier, sans fournir de fichier malveillant ni de procédure pour en construire un. Les essais se font dans votre laboratoire (DVWA propose un module d’envoi de fichiers, tout comme les laboratoires de la PortSwigger Web Security Academy), avec des fichiers de test inoffensifs.
Pourquoi l’upload est à risque
Le danger dépend de ce que le serveur fait du fichier après réception :
| Ce que fait le serveur | Risque potentiel |
|---|---|
| Stocke le fichier dans un dossier accessible par le web et le sert tel quel | Si le fichier est du contenu actif (script, page), il peut être exécuté ou interprété par le navigateur d’un visiteur |
| Laisse le serveur interpréter le fichier selon son extension | Un fichier exécutable côté serveur déposé dans un dossier d’exécution pourrait être lancé : impact maximal, prise de contrôle du serveur |
| Traite le fichier avec une bibliothèque (image, document, archive) | Des failles dans l’analyseur peuvent être déclenchées par un fichier mal formé |
| Utilise le nom du fichier fourni par l’utilisateur | Le nom peut contenir des séquences de remontée dans l’arborescence, écraser un fichier existant ou injecter du contenu |
| Accepte des fichiers très volumineux ou en grand nombre | Épuisement de l’espace disque ou de la mémoire (déni de service) |
| Affiche le fichier aux autres utilisateurs | XSS stocké via un fichier de type page web ou image contenant du script |
Le cas le plus grave est celui où un fichier déposé devient exécutable par le serveur. Il est rare sur des applications bien conçues, mais l’impact est tel qu’on le vérifie toujours.
Les contrôles que l’on évalue
Lors d’un test en laboratoire, on observe, avec des fichiers parfaitement inoffensifs (un petit fichier texte, une image ordinaire), comment l’application réagit sur chacun de ces points :
1. Le contrôle du type de fichier
L’application vérifie-t-elle ce qu’elle reçoit ? Trois éléments sont à distinguer :
- l’extension du nom de fichier (modifiable par l’utilisateur) ;
- le type déclaré dans la requête (
Content-Type, également modifiable) ; - le contenu réel du fichier (ses premiers octets, sa structure).
Un contrôle qui ne repose que sur l’extension ou sur le type déclaré est insuffisant : ce sont des déclarations du client. Un test simple et inoffensif : renommer un fichier texte avec l’extension d’une image et voir s’il est accepté ; ou l’inverse. Si l’application accepte un fichier qui n’est pas ce qu’il prétend être, le contrôle est faible.
2. Le stockage
Où le fichier est-il enregistré, et sous quel nom ? Les bonnes pratiques :
- stocker hors de la racine web, ou sur un service de stockage séparé ;
- renommer le fichier avec un nom généré par le serveur (aléatoire), sans reprendre le nom fourni ;
- ne pas permettre l’exécution dans le dossier de dépôt ;
- servir les fichiers par un chemin contrôlé, avec le bon type de contenu et l’en-tête
X-Content-Type-Options: nosniff.
3. Les limites
Taille maximale, nombre de fichiers, quota par utilisateur, fréquence des envois. Sans limites, la fonction devient un moyen de saturer le serveur.
4. Le nom de fichier et le chemin
Le nom fourni peut contenir des caractères de chemin. L’application ne doit jamais l’utiliser pour construire un chemin sur le disque sans l’avoir assaini, et ne devrait de préférence pas l’utiliser du tout.
5. L’accès aux fichiers déposés
Un fichier envoyé par A est-il téléchargeable par B ? Les adresses des fichiers sont-elles devinables ? C’est le sujet de l’IDOR (leçon 1 du chapitre 5) appliqué aux documents : l’autorisation doit être contrôlée à chaque téléchargement.
6. L’analyse du contenu
Pour les fichiers venant d’inconnus, une analyse antivirus ou antimalware côté serveur, et un retraitement (par exemple, redimensionner une image en la reconstruisant) permettent d’éliminer des contenus inattendus.
Démarche de test en laboratoire
- Comprendre la fonction : types autorisés, endroit où le fichier apparaît ensuite, qui peut le voir.
- Envoyer un fichier conforme (une image ordinaire) et suivre le trajet : quel nom, quelle adresse, quels en-têtes de réponse quand on l’affiche.
- Tester les contrôles avec des fichiers inoffensifs : un petit texte renommé, un fichier d’un type non prévu, un fichier très volumineux (dans la limite raisonnable de votre laboratoire).
- Observer le nommage : le nom est-il conservé tel quel ?
- Évaluer l’exécution possible : le dossier de dépôt interprète-t-il des fichiers actifs ? On le déduit de la configuration et de la documentation du labo ; on ne cherche pas à le prouver avec un fichier réellement dangereux.
- Documenter : contrôles présents, contrôles absents, impact potentiel.
Les laboratoires guidés de la PortSwigger Academy détaillent ces scénarios dans un cadre sûr, avec des consignes pas à pas. DVWA, au niveau « impossible », illustre ce que fait une bonne implémentation : vérification du contenu, renommage et nettoyage du fichier.
Un exemple de contrôle côté serveur
$maxOctets = 2 * 1024 * 1024;
$typesAutorises = ['image/jpeg' => 'jpg', 'image/png' => 'png'];
if ($_FILES['photo']['size'] > $maxOctets) { http_response_code(413); exit; }
// On détermine le type d'après le contenu réel, pas d'après le nom ni le type déclaré
$typeReel = mime_content_type($_FILES['photo']['tmp_name']);
if (!isset($typesAutorises[$typeReel])) { http_response_code(415); exit; }
// Nom généré par le serveur, stockage hors de la racine web
$nouveauNom = bin2hex(random_bytes(16)) . '.' . $typesAutorises[$typeReel];
move_uploaded_file($_FILES['photo']['tmp_name'], '/var/stockage-prive/' . $nouveauNom);
Ce code illustre les principes : limite de taille, type déterminé d’après le contenu, liste blanche, nom aléatoire, stockage privé. Dans un projet réel, on y ajoute le contrôle d’autorisation, l’analyse du contenu et un service de téléchargement dédié.
Défendre : le résumé
- Liste blanche de types autorisés, vérifiés d’après le contenu.
- Nom généré par le serveur, jamais le nom d’origine.
- Stockage hors de la racine web ou sur un stockage objet séparé, sans droit d’exécution.
- Limites de taille, de nombre et de fréquence.
- Service de téléchargement contrôlé, avec vérification d’autorisation et en-têtes appropriés.
- Analyse antimalware et, si possible, retraitement du fichier.
- Domaine séparé pour servir les contenus envoyés par les utilisateurs, afin d’isoler d’éventuels scripts du site principal.
Astuce
Chaque contrôle du tableau est un test dans votre liste de vérification. Après chacun, notez le résultat attendu : « le fichier est refusé », « le nom est remplacé », « le fichier n’est pas accessible depuis l’extérieur ». Les écarts sont vos constats.
À vous de jouer
- Dans DVWA (module d’envoi de fichiers, niveau bas), envoyez une image ordinaire. Relevez : le nom conservé, l’adresse d’accès, la réponse de l’application.
- Envoyez un fichier texte inoffensif avec l’extension d’une image, puis une image avec une extension non prévue. Que contrôle l’application ?
- Comparez avec le niveau « impossible » : listez les contrôles supplémentaires.
- Rédigez un constat sur la faiblesse du contrôle du type, avec correction.
Voir une solution
Au niveau bas, l’application ne vérifie que très peu ou pas du tout le type : elle conserve le nom d’origine et stocke dans un dossier accessible. Au niveau « impossible », vous trouverez une validation du contenu réel, un renommage, un retraitement de l’image et un stockage plus protégé. Constat type : « Le contrôle du type de fichier repose sur l’extension fournie par le client. Impact : des fichiers non prévus peuvent être stockés ; risque aggravé si le dossier permet l’exécution. Gravité moyenne à élevée. Correction : liste blanche vérifiée d’après le contenu, nom généré, stockage hors racine web sans exécution. »
À retenir
- Un upload accepte un contenu entièrement contrôlé par l’utilisateur : le risque dépend de ce que le serveur en fait.
- Le cas le plus grave est un fichier qui devient exécutable côté serveur ; le plus fréquent, un stockage et des contrôles faibles.
- L’extension et le type déclaré viennent du client : seul le contenu réel compte.
- Défense : liste blanche, nom généré, stockage privé sans exécution, limites, accès contrôlé, analyse.
- En test, on n’utilise que des fichiers inoffensifs, dans un laboratoire autorisé.
Dernière leçon de ce chapitre : une faille plus abstraite, qui concerne la manière dont les applications échangent des objets, la désérialisation.