CSRF et CORS : des failles banales aux conséquences catastrophiques
Le rapport PortSwigger Web Security 2024 est sans appel : les attaques Cross-Site Request Forgery (CSRF) représentent 36 % des failles web identifiées. L'OWASP Top 10 2021 place les failles d'injection (dont les injections via requêtes falsifiées) en troisième position des risques les plus critiques pour la sécurité des applications web. Ces chiffres ne reflètent que les failles documentées — la majorité des CSRF ne sont jamais détectées.
Une attaque CSRF réussie permet à un attaquant de faire effectuer à votre utilisateur authentifié n'importe quelle action sur votre site — virement bancaire, modification d'email, suppression de données — sans que l'utilisateur ne s'en aperçoive. Si votre application gère de l'argent, des données personnelles ou des actions irréversibles sans protection CSRF, vous êtes exposé.
Chez ADRD Consulting, la protection CSRF est non négociable sur tout formulaire POST. Voici notre implémentation complète, testée sur RecruteX, MailGuard et QualiTrack.
Comprendre CSRF en 2 minutes
Imaginez un formulaire de virement sur votre banque en ligne :
<form action="https://mabanque.fr/virement" method="POST">
<input name="montant" value="500">
<input name="destinataire" value="IBAN_MIEN">
</form>
Un attaquant crée une page piège avec ce formulaire caché, qui s'envoie automatiquement. Si vous visitez la page piège pendant que vous êtes connecté à votre banque, le virement s'effectue — votre navigateur envoie automatiquement votre cookie de session. C'est ça, CSRF : exploiter la confiance que le serveur accorde aux requêtes venant de votre navigateur.
Notre implémentation CSRF : hash_equals + expiration 30 minutes
La défense standard est le token CSRF : une valeur aléatoire générée côté serveur, incluse dans le formulaire, vérifiée à la soumission. Un attaquant ne peut pas connaître cette valeur puisqu'il ne peut pas lire vos cookies ni vos pages HTML (politique same-origin).
<?php
// includes/csrf.php — Gestion tokens CSRF ADRD
function csrf_generate(): string {
if (session_status() === PHP_SESSION_NONE) session_start();
$token = bin2hex(random_bytes(32));
$expiry = time() + 1800; // 30 minutes
$_SESSION['csrf_token'] = $token;
$_SESSION['csrf_expiry'] = $expiry;
return $token;
}
function csrf_verify(string $tokenSoumis): bool {
if (session_status() === PHP_SESSION_NONE) session_start();
// Token absent ou expiré
if (empty($_SESSION['csrf_token']) || empty($_SESSION['csrf_expiry'])) {
return false;
}
// Vérification expiration
if (time() > $_SESSION['csrf_expiry']) {
unset($_SESSION['csrf_token'], $_SESSION['csrf_expiry']);
return false;
}
// Comparaison timing-safe — JAMAIS ===
$valide = hash_equals($_SESSION['csrf_token'], $tokenSoumis);
// Invalider après usage (token à usage unique)
unset($_SESSION['csrf_token'], $_SESSION['csrf_expiry']);
return $valide;
}
function csrf_field(): string {
return '';
}
Utilisation dans un formulaire :
<form method="POST" action="/traitement">
<?= csrf_field() ?>
<input type="email" name="email">
<button type="submit">Envoyer</button>
</form>
Vérification côté serveur :
require_once 'includes/csrf.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!csrf_verify($_POST['csrf_token'] ?? '')) {
http_response_code(403);
die('Token CSRF invalide ou expiré.');
}
// Traitement du formulaire
}
SameSite cookies : la deuxième ligne de défense
Les cookies SameSite sont devenus le standard moderne de protection CSRF. Avec SameSite=Strict, le navigateur n'envoie jamais le cookie lors de requêtes cross-site — même si un attaquant génère une requête vers votre site depuis sa page piège.
// Configuration session PHP — à mettre en début de chaque script
ini_set('session.cookie_samesite', 'Strict'); // Protection CSRF maximale
ini_set('session.cookie_httponly', 1); // Protection XSS
ini_set('session.cookie_secure', 1); // HTTPS uniquement
Attention : SameSite=Strict bloque aussi les liens depuis des emails ou d'autres sites légitimes. Si votre application a des flows de connexion via liens externes, préférez SameSite=Lax (qui protège contre CSRF tout en permettant la navigation normale).
CORS : ne jamais mettre Allow-Origin: *
Le Cross-Origin Resource Sharing (CORS) est souvent mal configuré par excès de simplicité. Access-Control-Allow-Origin: * ouvre votre API à n'importe quelle origine — y compris des sites malveillants. Notre règle ADRD : toujours spécifier explicitement les domaines autorisés.
// Configuration CORS restrictive ADRD
function setCorsHeaders(): void {
$allowedOrigins = [
'https://adrd-consulting.com',
'https://app.adrd-consulting.com',
'https://recrutex.adrd-consulting.com'
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins, true)) {
header("Access-Control-Allow-Origin: {$origin}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-CSRF-Token');
header('Vary: Origin'); // Important pour le cache
}
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
}
Checklist ADRD — Protection CSRF/CORS
- ✅ Token CSRF sur tous les formulaires POST
- ✅
hash_equals()pour la comparaison — jamais=== - ✅ Expiration du token (30 min max)
- ✅ Token à usage unique (invalidé après vérification)
- ✅ Cookies
SameSite=StrictouLax - ✅ CORS : liste blanche des origines, jamais
* - ✅ Header
Vary: Originpour éviter les problèmes de cache - ✅ Vérification
Content-Typesur les API JSON
36 % des failles web sont des CSRF — et ce sont parmi les plus faciles à prévenir avec les bons automatismes. ADRD Consulting peut auditer votre application et implémenter ces protections. Demandez un audit de sécurité.