Introduction
La sécurité web est un aspect crucial du développement d'applications modernes. Avec l'augmentation constante des cyberattaques, il est essentiel de comprendre et d'implémenter les meilleures pratiques de sécurité dès le début du développement. Cet article explore les vulnérabilités les plus courantes et comment les prévenir.
Le Top 10 OWASP
L'Open Web Application Security Project (OWASP) maintient une liste des 10 risques de sécurité les plus critiques pour les applications web. Voici les principaux :
1. Injection (SQL, NoSQL, LDAP)
Les attaques par injection se produisent lorsque des données non fiables sont envoyées à un interpréteur comme partie d'une commande ou d'une requête.
Exemple d'Injection SQL vulnérable :
// VULNÉRABLE
const query = `SELECT * FROM users WHERE username = '${username}'
AND password = '${password}'`;
db.execute(query);
Protection avec requêtes préparées :
// SÉCURISÉ
const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
db.execute(query, [username, password]);
2. Broken Authentication
Les failles d'authentification permettent aux attaquants de compromettre les mots de passe, clés ou tokens de session.
Bonnes pratiques :
- Implémenter l'authentification multi-facteurs (MFA)
- Ne jamais stocker les mots de passe en clair
- Utiliser des algorithmes de hachage sécurisés (bcrypt, Argon2)
- Implémenter des politiques de mots de passe forts
- Limiter les tentatives de connexion
// Exemple avec bcrypt
const bcrypt = require('bcrypt');
const saltRounds = 10;
// Hachage du mot de passe
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Vérification
const match = await bcrypt.compare(inputPassword, hashedPassword);
3. Cross-Site Scripting (XSS)
XSS permet aux attaquants d'injecter des scripts malveillants dans des pages web vues par d'autres utilisateurs.
Types de XSS :
- Reflected XSS : Le script malveillant vient de la requête HTTP actuelle
- Stored XSS : Le script est stocké sur le serveur (base de données)
- DOM-based XSS : La vulnérabilité existe côté client
Protection :
// Échapper les données utilisateur
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// Utiliser Content Security Policy (CSP)
// En-tête HTTP :
// Content-Security-Policy: default-src 'self'; script-src 'self'
4. Cross-Site Request Forgery (CSRF)
CSRF force un utilisateur authentifié à exécuter des actions non désirées sur une application web.
Protection avec tokens CSRF :
// Générer un token CSRF
const csrfToken = generateSecureToken();
sessionStorage.setItem('csrfToken', csrfToken);
// Inclure dans les formulaires
<input type="hidden" name="csrf_token" value="${csrfToken}">
// Vérifier côté serveur
if (req.body.csrf_token !== req.session.csrfToken) {
throw new Error('Invalid CSRF token');
}
Sécurisation des APIs
1. Authentification et Autorisation
Utilisez des standards éprouvés comme OAuth 2.0 et JWT :
// Exemple avec JWT
const jwt = require('jsonwebtoken');
// Créer un token
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
// Middleware de vérification
function verifyToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Invalid token' });
}
req.user = decoded;
next();
});
}
2. Rate Limiting
Limitez le nombre de requêtes pour prévenir les abus :
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limite chaque IP à 100 requêtes par windowMs
message: 'Trop de requêtes, veuillez réessayer plus tard'
});
app.use('/api/', limiter);
HTTPS et Sécurité de Transport
Toujours utiliser HTTPS
HTTPS chiffre les communications entre le client et le serveur :
- Obtenir un certificat SSL/TLS (Let's Encrypt gratuit)
- Rediriger tout le trafic HTTP vers HTTPS
- Utiliser HSTS (HTTP Strict Transport Security)
// En-tête HSTS
app.use((req, res, next) => {
res.setHeader(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
next();
});
Sécurité des Données
1. Chiffrement des Données Sensibles
const crypto = require('crypto');
// Chiffrement
function encrypt(text, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return { encrypted, iv: iv.toString('hex'), authTag: authTag.toString('hex') };
}
// Déchiffrement
function decrypt(encrypted, key, iv, authTag) {
const decipher = crypto.createDecipheriv('aes-256-gcm', key, Buffer.from(iv, 'hex'));
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
2. Validation des Entrées
Toujours valider et assainir les entrées utilisateur :
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail().normalizeEmail(),
body('username').isAlphanumeric().trim().escape(),
body('age').isInt({ min: 0, max: 120 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Traiter les données validées
}
);
En-têtes de Sécurité HTTP
Configurez les en-têtes de sécurité appropriés :
const helmet = require('helmet');
app.use(helmet());
// Ou configurez manuellement
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
});
Gestion des Secrets et Variables d'Environnement
- Ne jamais commiter de secrets dans Git
- Utiliser des fichiers .env (avec .gitignore)
- Utiliser des services de gestion de secrets (AWS Secrets Manager, HashiCorp Vault)
- Rotation régulière des clés et tokens
// .env
DB_PASSWORD=super_secret_password
JWT_SECRET=another_secret_key
API_KEY=third_secret_key
// .gitignore
.env
.env.local
.env.*.local
// Utilisation
require('dotenv').config();
const dbPassword = process.env.DB_PASSWORD;
Logging et Monitoring
Surveillez votre application pour détecter les activités suspectes :
- Logger les tentatives de connexion échouées
- Monitorer les patterns d'utilisation anormaux
- Alerter sur les événements de sécurité critiques
- Conserver les logs pour l'analyse forensique
- Ne jamais logger de données sensibles (mots de passe, tokens)
Checklist de Sécurité
- ✅ Utiliser HTTPS partout
- ✅ Implémenter une authentification forte
- ✅ Valider toutes les entrées utilisateur
- ✅ Utiliser des requêtes préparées (SQL)
- ✅ Échapper les sorties (XSS)
- ✅ Implémenter la protection CSRF
- ✅ Configurer les en-têtes de sécurité
- ✅ Limiter le taux de requêtes (rate limiting)
- ✅ Chiffrer les données sensibles
- ✅ Maintenir les dépendances à jour
- ✅ Effectuer des audits de sécurité réguliers
- ✅ Former l'équipe aux bonnes pratiques
Outils de Sécurité
Utilisez ces outils pour améliorer la sécurité :
- npm audit : Vérifier les vulnérabilités des dépendances
- Snyk : Scanner de vulnérabilités
- OWASP ZAP : Scanner de sécurité web
- SonarQube : Analyse de qualité et sécurité du code
- Burp Suite : Test de pénétration
Conclusion
La sécurité web n'est pas une fonctionnalité, c'est un processus continu. Il est crucial d'intégrer les pratiques de sécurité dès le début du développement et de rester à jour avec les dernières menaces et solutions. Rappelez-vous : une application est aussi sûre que son maillon le plus faible. Investir dans la sécurité aujourd'hui peut vous éviter des problèmes coûteux demain.
Attention : La sécurité évolue constamment. Consultez régulièrement le site OWASP et suivez les bulletins de sécurité pour rester informé des nouvelles vulnérabilités et meilleures pratiques.