Validation des fichiers uploadés
Notions théoriques
1. Introduction
Lorsqu'un utilisateur envoie un fichier à une application web, il est essentiel de valider ce fichier avant de l'accepter. Une mauvaise gestion des fichiers uploadés peut entraîner des risques de sécurité, des erreurs système et des problèmes de stockage.
2. Risques liés à l'upload de fichiers
L'upload de fichiers non sécurisés peut exposer une application à plusieurs menaces :
- Exécution de code malveillant : Un fichier
.php
,.exe
ou.sh
malveillant peut être exécuté sur le serveur. - Attaque par dépassement de capacité : Un fichier trop volumineux peut saturer l'espace disque ou la mémoire.
- Injection de scripts (XSS ou SQL) : Un fichier contenant du code injecté peut être utilisé pour attaquer la base de données ou manipuler l'affichage.
- Falsification de type MIME : Un fichier
.jpg
peut en réalité être un script.php
déguisé.
3. Bonnes pratiques pour sécuriser l'upload de fichiers
Pour éviter ces risques, plusieurs règles doivent être respectées :
-
Limiter les types de fichiers autorisés
- Vérifier l'extension (
.jpg
,.png
,.pdf
, etc.). - Vérifier le type MIME (
image/jpeg
,application/pdf
, etc.). - Utiliser une liste blanche des formats acceptés.
- Vérifier l'extension (
-
Limiter la taille des fichiers
- Définir une taille maximale (ex. : 2 Mo pour une image).
- Rejeter les fichiers trop volumineux.
-
Renommer les fichiers
- Générer un nom unique pour éviter les conflits et les exécutions de scripts (
UUID
, horodatage, hash).
- Générer un nom unique pour éviter les conflits et les exécutions de scripts (
-
Stocker les fichiers en dehors du dossier public
- Empêcher leur exécution directe via le navigateur.
-
Analyser le contenu des fichiers
- Vérifier si un fichier image contient réellement une image et non du code injecté.
-
Utiliser un dossier temporaire
- Scanner les fichiers avant de les déplacer vers leur emplacement définitif.
Exemple pratique
Il est possible de sécuriser l'upload de fichiers avec Node.js et Express.js :
- en utilisant
multer
pour gérer les fichiers - et en effectuant des vérifications sur leur type et leur taille.
1. Installation des dépendances
npm install express multer path fs-extra
2. Mise en place du serveur avec validation des fichiers
const express = require("express");
const multer = require("multer");
const path = require("path");
const fs = require("fs-extra");
const app = express();
// Configuration de Multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
cb(null, uniqueSuffix + path.extname(file.originalname));
}
});
// Vérification du type de fichier et de la taille
const upload = multer({
storage: storage,
limits: { fileSize: 2 * 1024 * 1024 }, // 2 Mo
fileFilter: (req, file, cb) => {
const fileTypes = /jpeg|jpg|png|pdf/;
const extName = fileTypes.test(path.extname(file.originalname).toLowerCase());
const mimeType = fileTypes.test(file.mimetype);
if (extName && mimeType) {
return cb(null, true);
} else {
return cb(new Error("Type de fichier non autorisé"));
}
}
});
// Route d'upload
app.post("/upload", upload.single("file"), (req, res) => {
res.json({ message: "Fichier uploadé avec succès", file: req.file });
});
// Démarrer le serveur
app.listen(3000, () => {
console.log("Serveur démarré sur http://localhost:3000");
});
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Sécurisation de l'upload de fichiers avec Node.js et Multer
L'objectif est de créer un serveur sécurisé qui permet l'upload de fichiers avec validation stricte.
L'application devra :
- Limiter les types de fichiers acceptés (
.jpg
,.png
,.pdf
). - Restreindre la taille des fichiers à 2 Mo maximum.
- Renommer les fichiers pour éviter les conflits et les attaques.
- Stocker les fichiers dans un dossier sécurisé.
- Rejeter les fichiers suspects et afficher un message d'erreur clair.
Étape 1 : Initialisation du projet
Avant d’écrire du code, il est nécessaire de préparer l’environnement de travail.
Instructions :
- Ouvrir un terminal et naviguer vers un dossier de travail.
- Créer un nouveau dossier pour le projet :
mkdir secure-upload
cd secure-upload
- Initialiser un projet Node.js :
npm init -y
- Installer les dépendances nécessaires :
npm install express multer path fs-extra dotenv
- Vérifier que les fichiers suivants sont bien présents dans le dossier :
package.json
(créé automatiquement)node_modules/
(dossier contenant les bibliothèques installées)
Vérifier si :
- Un projet Node.js est correctement initialisé.
- Les modules Express, Multer, Path, fs-extra et dotenv sont installés.
Étape 2 : Configuration des variables d’environnement
Pour rendre l’application plus flexible, il est utile d’utiliser un fichier .env
pour stocker les paramètres.
Instructions :
- Créer un fichier
.env
à la racine du projet :
touch .env
- Ajouter les variables suivantes :
PORT=4000
UPLOAD_DIR=uploads
MAX_FILE_SIZE=2
ALLOWED_FILE_TYPES=jpeg,jpg,png,pdf
- Modifier le fichier
package.json
pour ajouter un script de démarrage :
"scripts": {
"start": "node server.js"
}
Vérifier si :
- Un fichier
.env
est créé pour stocker les variables d’environnement. - Le fichier
package.json
est mis à jour avec un script de démarrage.
Étape 3 : Création du serveur Express.js
Le serveur doit être capable de recevoir des fichiers et de les stocker de manière sécurisée.
Instructions :
- Créer un fichier
server.js
et ajouter le code suivant :
require("dotenv").config();
const express = require("express");
const multer = require("multer");
const path = require("path");
const fs = require("fs-extra");
const app = express();
// Charger les variables d'environnement
const UPLOAD_DIR = process.env.UPLOAD_DIR || "uploads";
const MAX_FILE_SIZE = parseInt(process.env.MAX_FILE_SIZE) * 1024 * 1024;
const ALLOWED_FILE_TYPES = process.env.ALLOWED_FILE_TYPES.split(",");
// Vérifier que le dossier d'upload existe
fs.ensureDirSync(UPLOAD_DIR);
// Configuration de Multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, UPLOAD_DIR);
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
cb(null, uniqueSuffix + path.extname(file.originalname));
}
});
// Vérification du type de fichier et de la taille
const upload = multer({
storage: storage,
limits: { fileSize: MAX_FILE_SIZE },
fileFilter: (req, file, cb) => {
const extName = ALLOWED_FILE_TYPES.includes(path.extname(file.originalname).slice(1));
const mimeType = ALLOWED_FILE_TYPES.includes(file.mimetype.split("/")[1]);
if (extName && mimeType) {
cb(null, true);
} else {
cb(new Error("Type de fichier non autorisé"));
}
}
});
// Route d'upload
app.post("/upload", upload.single("file"), (req, res) => {
res.json({ message: "Fichier uploadé avec succès", file: req.file });
});
// Gestion des erreurs
app.use((err, req, res, next) => {
res.status(400).json({ error: err.message });
});
// Démarrer le serveur
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur démarré sur http://localhost:${PORT}`);
});
Vérifier si :
- Un serveur Express.js est mis en place avec Multer pour gérer l’upload.
- Le fichier est stocké dans un dossier sécurisé défini par
.env
. - Une gestion des erreurs est ajoutée pour rejeter les fichiers non conformes.
Étape 4 : Test de l'upload
Il est maintenant temps de tester si le serveur fonctionne correctement.
Instructions :
- Lancer le serveur :
npm start
- Ouvrir Postman ou utiliser curl pour tester l'upload :
curl -X POST -F "file=@test.jpg" http://localhost:4000/upload
-
Vérifier la réponse du serveur :
- Si le fichier est accepté, un message de succès doit s’afficher.
- Si le fichier est trop volumineux ou d’un type non autorisé, un message d’erreur doit apparaître.
-
Vérifier que le fichier est bien sauvegardé dans le dossier
uploads/
.
Vérifier si :
- L'upload fonctionne et le fichier est sauvegardé dans
uploads/
. - Les fichiers non conformes sont rejetés avec un message d’erreur clair.
Étape 5 : Sécurisation avancée
Pour renforcer la sécurité, il est possible d'ajouter des protections supplémentaires.
Instructions :
- Empêcher l’exécution des fichiers uploadés
- Créer un fichier
.htaccess
dans le dossieruploads/
avec le contenu suivant :
- Créer un fichier
<FilesMatch ".*">
Order Allow,Deny
Deny from all
</FilesMatch>
- Nettoyer les fichiers temporaires
- Modifier
server.js
pour supprimer les fichiers rejetés :
- Modifier
app.use((err, req, res, next) => {
if (req.file) {
fs.unlinkSync(req.file.path);
}
res.status(400).json({ error: err.message });
});
- Limiter le nombre d’uploads par utilisateur
- Ajouter un middleware pour limiter les requêtes :
const rateLimit = require("express-rate-limit");
const uploadLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 requêtes maximum
message: "Trop d'uploads, veuillez réessayer plus tard."
});
app.post("/upload", uploadLimiter, upload.single("file"), (req, res) => {
res.json({ message: "Fichier uploadé avec succès", file: req.file });
});
Vérifier si :
- Un fichier
.htaccess
empêche l'exécution des fichiers uploadés. - Les fichiers rejetés sont automatiquement supprimés.
- Un Rate Limiting empêche les abus d’upload.
Conclusion
Ce TP permet de comprendre comment valider et sécuriser l’upload de fichiers en appliquant :
- Des restrictions sur les types de fichiers et la taille.
- Un stockage sécurisé et une gestion des erreurs.
- Des protections avancées contre les attaques.