CSRF (CWE-352, OWASP A01:2021) : force un utilisateur authentifié à exécuter des requêtes non souhaitées en exploitant l'envoi automatique des cookies par le navigateur.
TL;DR
Le Cross-Site Request Forgery est une attaque dans laquelle un adversaire trompe le navigateur d'un utilisateur authentifié pour qu'il soumette une requête HTTP modifiant l'état d'une application web sur laquelle la victime est connectée. L'attaquant n'a pas besoin de voler le cookie de session — le navigateur le livre automatiquement. Trois conditions doivent être simultanément réunies pour que l'attaque réussisse : (1) une action privilégiée existe sur la cible — changement de mot de passe, virement de fonds, paramètre administrateur ; (2) l'application s'appuie exclusivement sur les cookies de session sans vérification complémentaire ; (3) tous les paramètres de la requête sont prévisibles ou contrôlables par l'attaquant à l'avance.
Le CSRF est classifié CWE-352 (Cross-Site Request Forgery). Il apparaît sous OWASP A01:2021 (Broken Access Control) lorsqu'il contourne l'application des contrôles d'accès sur les endpoints modifiant l'état. Il a été retiré en tant que catégorie autonome de l'OWASP Top 10 en 2021 — non parce qu'il est résolu, mais parce que les valeurs par défaut SameSite dans les navigateurs modernes ont réduit la prévalence sur les applications standard. Les applications sans cookies de session sensibles au SameSite ou sans tokens explicites restent pleinement exploitables en 2025-2026.
Le CSRF se distingue du XSS : le XSS injecte un script qui lit des données et peut voler des tokens ; le CSRF forge des requêtes sortantes sans rien lire. Le CSRF se distingue du SSRF : le SSRF instrumentalise la position réseau du serveur lui-même ; le CSRF instrumentalise la session navigateur de la victime. Le CSRF se distingue du Clickjacking : le Clickjacking superpose un élément d'interface transparent pour détourner le clic de la victime ; le CSRF déclenche la requête invisiblement sans aucun clic.
L'attaque exploite l'envoi automatique des cookies par le navigateur. Un formulaire caché ou une requête de ressource sur la page de l'attaquant pousse le navigateur de la victime à envoyer une requête vers l'application cible — avec le cookie de session de la victime — avant que la victime n'ait la moindre possibilité de s'en apercevoir.
L'attaque se déroule en quatre étapes :
Un payload CSRF POST concret déclenchant un changement de mot de passe :
POST /compte/changer-mot-de-passe HTTP/1.1
Host: cible.exemple.com
Content-Type: application/x-www-form-urlencoded
Cookie: session=abc123xyz
nouveau_mdp=P%40ssw0rd_attaquant&confirmer_mdp=P%40ssw0rd_attaquantLa page de l'attaquant qui génère cela :
<!-- Auto-soumis au chargement de la page — aucune interaction utilisateur requise -->
<form id="csrf" action="https://cible.exemple.com/compte/changer-mot-de-passe"
method="POST" style="display:none;">
<input name="nouveau_mdp" value="P@ssw0rd_attaquant">
<input name="confirmer_mdp" value="P@ssw0rd_attaquant">
</form>
<script>document.getElementById("csrf").submit();</script>| Variante | Technique | Impact | Page dédiée |
|---|---|---|---|
| GET classique | <img>, window.location, <a> — GET d'état via navigation de premier niveau | Virement, suppression de compte | /learn/csrf-get |
| POST (formulaire) | Formulaire caché auto-soumis avec form-urlencoded ou multipart/form-data | Changement de mot de passe, email, action admin | /learn/csrf-post |
| JSON CSRF | Formulaire enctype="text/plain" envoyant un corps JSON ; le serveur l'analyse quel que soit le Content-Type | Mutation d'état API, mutation GraphQL | /learn/csrf-json |
| Contournement SameSite | Fenêtre Lax+POST 2 min, remplacement de méthode (_method=POST), gadget de redirection côté client, XSS domaine frère | Contourne la principale défense moderne | /learn/csrf-samesite-bypass |
| Login CSRF | Forcer la victime à s'authentifier en tant que compte de l'attaquant — aucun token sur l'endpoint pré-authentification | Collecte de données, liaison de compte OAuth | /learn/csrf-login |
| CSRF OAuth state | Paramètre state absent ou statique dans le flux OAuth — injection de code dans la session victime | Prise de contrôle de compte via identité sociale liée | /learn/csrf-login |
Le CSRF GET classique cible le sous-ensemble d'applications qui effectuent des changements d'état sur des requêtes GET (anti-pattern mais courant dans le code hérité). Les cookies SameSite=Lax sont envoyés lors des navigations GET de premier niveau — une redirection window.location ou un clic sur <a href> suffit. Les balises <img> déclenchent des GET de sous-ressources auxquels les cookies Lax ne sont pas joints ; le vrai vecteur de contournement est la navigation de premier niveau.
Le JSON CSRF utilise enctype="text/plain" pour construire un corps de formulaire qui se lit comme du JSON valide. Le navigateur envoie Content-Type: text/plain — un type CORS « simple » qui ne déclenche aucun preflight — tandis que le corps payload est {"action":"deleteAccount","id":123,"x":"="}. Les serveurs qui analysent le corps JSON sans valider le Content-Type exécutent la mutation. CVE-2024-4994 (GitLab GraphQL) et CVE-2025-68604 (WPGraphQL) suivent tous les deux ce mécanisme.
SameSite est la principale défense CSRF appliquée par le navigateur. Comprendre exactement ce qu'il bloque — et ce qu'il ne bloque pas — est critique pour les tests offensifs comme la configuration défensive.
| Cookie SameSite | Formulaire POST intersite | Navigation GET premier niveau | GET sous-ressource <img> | Notes |
|---|---|---|---|---|
Strict | Bloqué | Bloqué | Bloqué | Le plus sûr — peut casser les flux OAuth/SSO retour arrière |
Lax (défaut Chrome 80+) | Bloqué | Vulnérable | Bloqué | Les GET d'état restent exposés |
Lax (implicite, nouveau cookie, ≤120 s) | Vulnérable | Vulnérable | Bloqué | Fenêtre de grâce Chrome — voir ci-dessous |
None; Secure | Vulnérable | Vulnérable | Vulnérable | Requis pour les intégrations intersites (widgets de paiement, OAuth) |
| Absent (héritage pré-Chrome 80) | Vulnérable | Vulnérable | Vulnérable | Chrome 80+ applique Lax par défaut pour les nouveaux cookies |
Le durcissement par préfixe de cookie ajoute une couche de protection contre les attaques d'injection de cookie par sous-domaine :
| Préfixe | Exigences | Protection |
|---|---|---|
__Host- | Secure + Path=/ + aucun attribut Domain | Impossible à définir depuis les sous-domaines — le plus robuste |
__Secure- | Uniquement l'attribut Secure | Prévient l'injection de cookie HTTP |
| Aucun | Aucune contrainte | Vulnérable à l'injection de cookie par sous-domaine |
Cookie de session optimal : Set-Cookie: __Host-SID=<token>; Path=/; Secure; HttpOnly; SameSite=Strict
Seulement 3,52 % des sites web implémentent un attribut SameSite (audit 2024). Cela signifie que plus de 96 % des applications web s'appuient uniquement sur les tokens CSRF comme unique défense.
Fenêtre de grâce SameSite=Lax de 120 secondes : Chrome applique une exception Lax+POST de 2 minutes aux cookies émis sans attribut SameSite explicite. Un attaquant qui force une nouvelle authentification — en ouvrant une URL de déconnexion dans un popup, puis en soumettant immédiatement le payload CSRF — exploite cette fenêtre. Le cookie de session nouvellement émis n'a aucune application SameSite pendant 120 secondes. Atténuation : utiliser un attribut SameSite=Strict explicite ; ne jamais omettre SameSite.
Contournement par remplacement de méthode (SameSite=Lax) : Des frameworks comme Symfony, Rails et Laravel acceptent _method=POST ou X-HTTP-Method-Override: POST dans les requêtes GET. Un attaquant envoie un GET avec ?_method=POST&email=attaquant@evil.com — le navigateur joint les cookies Lax au GET, et le framework l'exécute comme un POST. Lab PortSwigger : « SameSite Lax bypass via method override ».
Gadget de redirection côté client (contournement SameSite=Strict) : Les redirections côté client (JavaScript window.location = url_param) s'exécutent dans l'origine cible — le navigateur traite la requête suivante comme same-site, non intersite. Les cookies SameSite=Strict sont envoyés. Une redirection ouverte sur https://cible.com/redirect?url=/compte/email?email=attaquant@x.com exploite cela.
Attaque par domaine frère : Same-site est défini au niveau eTLD+1. Un XSS sur un sous-domaine assets.cible.com peut forger des requêtes vers app.cible.com traitées comme same-site — contournant totalement SameSite=Strict et SameSite=Lax. Cela invalide également les mécanismes naïfs de double cookie soumis parce que l'attaquant peut injecter un cookie correspondant via le sous-domaine compromis.
Contournement par omission du token : Si un serveur valide le token CSRF uniquement lorsque le champ est présent (plutôt qu'en imposant sa présence), supprimer entièrement le champ csrf_token / authenticity_token / _token du formulaire contourne la protection. CVE-2023-47640 (Grails Framework, CVSS 8.8) exposait exactement ce mécanisme.
Origin null via iframe sandbox : Une iframe sandbox avec allow-scripts mais sans allow-same-origin envoie Origin: null. Si le serveur applique une logique if (!origin) allow(), le CSRF réussit malgré SameSite=Lax.
CVE-2024-23897 — Jenkins CLI lecture de fichier arbitraire → forge de token CSRF (CVSS 9.8, CISA KEV)
Jenkins ≤ 2.441 et LTS ≤ 2.426.2. L'analyseur CLI args4j remplace les arguments @<chemin_fichier> par le contenu du fichier par défaut. Des attaquants non authentifiés lisaient /var/jenkins_home/secrets/master.key et le secret de token CSRF, puis forgeaient des tokens CSRF valides pour exécuter des actions Jenkins CLI privilégiées sans interaction utilisateur. Chaîne d'attaque : lecture fichier → extraction token → forge CSRF → RCE. Ajouté au catalogue CISA KEV en août 2024, échéance 9 septembre 2024. Corrigé dans Jenkins 2.442 / LTS 2.426.3.
CVE-2024-20252 / CVE-2024-20254 — Cisco Expressway CSRF (CVSS 9.6 Critical) Cisco Expressway-C et Expressway-E, toutes les versions antérieures à 14.3.41 et 15.0.01. Des protections CSRF insuffisantes dans l'API de gestion web permettaient à un attaquant distant non authentifié de tromper un administrateur en suivant un lien forgé, exécutant des actions arbitraires au niveau de privilège administrateur — y compris des modifications de configuration système et la création de comptes privilégiés. CVE-2024-20254 exploitable dans la configuration par défaut ; aucune solution de contournement disponible. Publié en février 2024.
CVE-2024-4994 — GitLab GraphQL CSRF (CVSS 8.1, CWE-352) GitLab CE/EE 16.1.0 à 17.1.0. L'API GraphQL acceptait des types de contenu exploitables par CSRF (application/x-www-form-urlencoded, multipart/form-data) sans exigence de preflight, permettant à un attaquant non authentifié d'exécuter des mutations GraphQL arbitraires au nom d'une victime authentifiée. Impact : C:H/I:H. HackerOne #1122408 (prime de 3 370 $) a documenté le chemin de mutation GET. CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N.
CVE-2024-21690 — Atlassian Confluence CSRF + XSS réfléchi (CVSS 8.2) Confluence Data Center et Server 7.19.0 à 8.9.0. Combinaison de vulnérabilités CSRF et XSS réfléchi permettant à des attaquants non authentifiés d'exécuter du HTML/JavaScript arbitraire et de contraindre des utilisateurs authentifiés à effectuer des actions d'état non souhaitées. Corrigé dans 7.19.26+, 8.5.14+, 9.0.1+.
CVE-2024-49038 — Microsoft Copilot Studio CSRF PostMessage (CVSS 9.6)
Validation d'origine incorrecte dans le gestionnaire postMessage de Microsoft Copilot Studio permettant une élévation de privilèges via des messages forgés depuis une page intersite. Un attaquant contrôle une page dans une iframe ou un popup, envoie un message à la fenêtre Copilot Studio sans spécifier targetOrigin, et le récepteur exécute l'action sans valider event.origin. Publié le 26 novembre 2024.
CVE-2025-68604 — WPGraphQL CSRF (CVSS 5.4) WPGraphQL ≤ 2.5.3. Une page distante amène le navigateur authentifié de la victime à soumettre une requête vers l'endpoint GraphQL, exécutant des mutations au nom de la victime — création de comptes utilisateurs non autorisés, modification de contenu, élévation de privilèges. Corrigé dans WPGraphQL 2.5.4. Publié en mai 2026.
CVE-2022-24734 — MyBB Forum CSRF → RCE (CVSS 8.8)
MyBB < 1.8.30. CSRF sur l'action d'upload de fichier du panneau administrateur combiné avec SameSite=None sur le cookie de session admin et l'absence de validation de token CSRF. Impact : exécution de code arbitraire via écriture de fichier contrôlé par l'attaquant.
HackerOne #1497169 — GitHub Enterprise Management Console CSRF (10 000 $) Contournement de la protection CSRF dans la console de gestion de serveur GitHub Enterprise permettant des actions arbitraires sur l'infrastructure enterprise au niveau de privilège administrateur. La prime la plus élevée connue pour un CSRF sur HackerOne.
HackerOne #834366 — HackerOne.com Login CSRF (500 $)
Le token authenticity_token sur la propre page de connexion de HackerOne n'était pas correctement vérifié côté serveur, permettant la connexion via CSRF sans token valide — démontrant que même les plateformes axées sur la sécurité ratent la protection CSRF pré-authentification.
HackerOne #2326194 — Argo CD CSRF → Compromission cluster Kubernetes (4 660 $) Une validation insuffisante des requêtes dans l'interface de gestion GitOps d'Argo CD permettait au CSRF d'affecter la configuration complète du cluster Kubernetes — compromission totale du cluster via un seul exploit CSRF.
SameSite, chercher les préfixes __Host- ou __Secure-. L'absence de SameSite sous Chrome 80+ = Lax implicite (la fenêtre de 2 min s'applique quand même aux nouveaux cookies).csrf_token=) — accepté ou rejeté ?application/json en text/plain sur les endpoints API JSON. Observer si le serveur analyse le corps quel que soit le Content-Type.application/x-www-form-urlencoded.state ; tester avec des valeurs de state absentes, vides et statiques.Origin avec une valeur intersite, vérifier que le serveur la rejette.Burp Suite Pro inclut un générateur de PoC CSRF et une règle de scanner actif. OWASP ZAP règle de scan actif 20012 (Anti-CSRF Tokens Scanner) vérifie l'absence de tokens sur les endpoints POST. XSRFProbe CLI effectue une analyse approfondie des tokens CSRF incluant l'omission, le token vide, le token aléatoire et la réutilisation entre utilisateurs. Semgrep règles python.django.security.audit.django-no-csrf-token et équivalents spécifiques aux frameworks détectent les décorateurs @csrf_exempt manquants et l'absence de middleware de token en analyse statique.
BreachVex détecte le CSRF via un sélecteur de contournement sensible aux segments qui prévient les faux positifs sur les endpoints de validation en lecture seule (/check, /validate, /preview) tout en exécutant des sondes cross-origine différentielles sur tous les chemins modifiant l'état. Le scanner implémente une immunité bearer/clé-API — les endpoints utilisant une authentification par en-tête sont correctement identifiés comme immunisés. La sonde GET SameSite=Lax utilise une différentielle de vidage de cookie jar : GET avec auth vs. GET sans cookies — confirmé HIGH lorsque la requête authentifiée réussit alors que la requête sans cookie est rejetée avec un 401/403.
Les en-têtes Fetch Metadata (Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest) sont joints par tous les navigateurs modernes (couverture 98%+ en 2024-2025) et permettent l'application d'une politique d'isolation de ressources côté serveur. Rejeter toute requête intersite non-navigation avant qu'elle n'atteigne la logique applicative : if Sec-Fetch-Site == "cross-site" and method not in SAFE_METHODS: rejeter 403. Cette défense ne nécessite aucune gestion de token et bloque le CSRF de manière transparente.
Le serveur génère un token aléatoire cryptographiquement lié à la session (minimum 128 bits via CSPRNG) et l'intègre dans chaque formulaire modifiant l'état. Le serveur valide le token à la soumission — la politique de même origine empêche l'attaquant de lire le token en intersite.
# FastAPI — génération et validation du token
import secrets
from fastapi import Request, HTTPException, Form
def generer_token_csrf(session: dict) -> str:
token = secrets.token_urlsafe(32) # 256 bits d'entropie
session["csrf_token"] = token
return token
async def valider_csrf(request: Request, csrf_token: str = Form(...)):
token_session = request.session.get("csrf_token")
if not token_session or not secrets.compare_digest(csrf_token, token_session):
raise HTTPException(status_code=403, detail="Token CSRF invalide")Implémentations spécifiques aux frameworks :
| Framework | Nom du token CSRF | Mécanisme |
|---|---|---|
| Django | csrfmiddlewaretoken | CsrfViewMiddleware — actif par défaut ; ne jamais utiliser @csrf_exempt |
| Rails | authenticity_token | protect_from_forgery — actif par défaut ; le mode API l'exclut |
| Laravel | _token | Directive Blade @csrf ; middleware VerifyCsrfToken |
| ASP.NET | __RequestVerificationToken | Attribut [ValidateAntiForgeryToken] |
| Spring Security | _csrf | CookieCsrfTokenRepository.withHttpOnlyFalse() |
| Express | _csrf | Package csrf-csrf (csurf déprécié depuis 2022) |
| Angular | Cookie XSRF-TOKEN / en-tête X-XSRF-TOKEN | HttpClient lit et envoie automatiquement |
// Express.js — csrf-csrf (remplacement moderne de csurf déprécié)
import { doubleCsrf } from "csrf-csrf";
const { generateToken, doubleCsrfProtection } = doubleCsrf({
getSecret: () => process.env.CSRF_SECRET,
cookieName: "__Host-psifi.x-csrf-token",
cookieOptions: { sameSite: "strict", secure: true, httpOnly: true },
});
app.get("/formulaire", (req, res) => {
res.render("formulaire", { csrfToken: generateToken(req, res) });
});
// Applique le middleware — rejette les requêtes sans token valide
app.post("/compte/email", doubleCsrfProtection, requireAuth, async (req, res) => {
await db.updateEmail(req.user.id, req.body.email);
res.json({ success: true });
});Pour les microservices sans état et les SPA où stocker des tokens en session serveur est impraticable, signer le token avec HMAC-SHA256 lié à l'identifiant de session et un secret serveur. La variante naïve (valeur aléatoire dans cookie + paramètre, sans HMAC) est vulnérable à l'injection de cookie depuis des sous-domaines compromis.
import hmac, hashlib, secrets, base64
def creer_token_csrf(session_id: str, secret_serveur: bytes) -> str:
nonce = secrets.token_bytes(16)
sig = hmac.new(secret_serveur,
msg=(session_id.encode() + nonce),
digestmod=hashlib.sha256).digest()
return base64.urlsafe_b64encode(sig + nonce).decode()
def verifier_token_csrf(token: str, session_id: str, secret_serveur: bytes) -> bool:
raw = base64.urlsafe_b64decode(token.encode())
sig, nonce = raw[:32], raw[32:]
attendu = hmac.new(secret_serveur,
msg=(session_id.encode() + nonce),
digestmod=hashlib.sha256).digest()
return hmac.compare_digest(sig, attendu)Définir le cookie avec le préfixe __Host- : Set-Cookie: __Host-csrf=<token>; Secure; Path=/; SameSite=Strict
Le préfixe __Host- empêche les cookies injectés par un sous-domaine de remplacer le cookie CSRF légitime — bloquant le contournement du double cookie soumis par domaine frère.
Toujours définir un attribut SameSite explicite. Ne jamais l'omettre — l'omission déclenche la fenêtre de grâce Chrome Lax+POST de 120 secondes.
Set-Cookie: __Host-SID=<token>; Path=/; Secure; HttpOnly; SameSite=StrictSameSite=Strict est la valeur par défaut correcte pour les panneaux d'administration, les applications bancaires et les applications de santé. Il peut casser les flux de connexion OAuth/SSO où la redirection du serveur d'autorisation arrive en intersite — dans ce cas, utiliser SameSite=Lax avec un token CSRF explicite comme deuxième couche.
Tout en-tête personnalisé — X-Requested-With: XMLHttpRequest, X-CSRF-Token: <valeur> — force un preflight CORS pour les requêtes intersites. Les navigateurs ne peuvent pas joindre d'en-têtes personnalisés aux requêtes simples intersites. Côté serveur : valider que l'en-tête personnalisé est présent sur tous les appels AJAX modifiant l'état.
# FastAPI — politique d'isolation de ressources Fetch Metadata
from fastapi import Request, HTTPException
METHODES_SURES = {"GET", "HEAD", "OPTIONS"}
SITES_AUTORISES = {"same-origin", "same-site", "none"}
async def politique_isolation_ressources(request: Request):
site = request.headers.get("Sec-Fetch-Site", "")
mode = request.headers.get("Sec-Fetch-Mode", "")
dest = request.headers.get("Sec-Fetch-Dest", "")
if request.method in METHODES_SURES or site in SITES_AUTORISES:
return
if mode == "navigate" and dest not in ("object", "embed"):
return # autoriser la navigation navigateur de premier niveau
raise HTTPException(status_code=403, detail="Requête intersite rejetée")Le CSRF de déconnexion et la suppression de compte via GET sont des vecteurs d'attaque réels. Les requêtes GET modifiant l'état violent RFC 9110 §9.2 (les méthodes sûres NE DOIVENT PAS modifier l'état) mais restent courantes dans le code hérité et les panneaux d'administration. Un attaquant intègre <a href="https://cible.com/deconnexion"> ou utilise window.location pour déclencher un GET de premier niveau — les cookies SameSite=Lax sont envoyés. Résultat : terminaison de session (DoS) ou suppression de données. Tout endpoint modifiant l'état doit exiger POST (ou PUT/PATCH/DELETE) — jamais GET.
L'attaque BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext) applique des techniques d'oracle de compression de type CRIME aux réponses HTTPS. Lorsqu'un serveur compresse ses réponses HTTP avec gzip ou Brotli et qu'une valeur secrète (comme un token CSRF) est reflétée aux côtés d'une entrée contrôlée par l'attaquant dans le même corps de réponse, l'attaquant peut déterminer de manière itérative la valeur du token en mesurant les variations de longueur du texte chiffré — exploitant la propriété de l'encodage Huffman selon laquelle les séquences d'octets correspondantes se compriment plus efficacement. Chaque devinette partageant des octets avec le secret produit un texte chiffré légèrement plus court, révélant le token un caractère à la fois via ~3 000 à 4 000 requêtes adaptatives.
Les atténuations sont indépendantes et complémentaires : (1) masquage de token par requête — XOR le token CSRF de session avec un nonce aléatoire à chaque réponse et transmettre token_masqué = token XOR nonce ainsi que nonce ; le serveur récupère token = token_masqué XOR nonce — cela rend chaque valeur reflétée unique, détruisant l'oracle ; (2) utiliser la librairie csrf-csrf (Node.js) qui implémente ce masquage par défaut ; (3) désactiver la compression HTTP (Content-Encoding: identity) sur toute réponse reflétant un secret aux côtés d'une entrée contrôlée par l'utilisateur ; (4) combiner avec Content-Security-Policy pour empêcher les lectures cross-origin assistées par BREACH via l'injection de sous-ressources.
Défense secondaire pour les environnements où la gestion de token n'est pas possible. Valider Origin (préféré) ou Referer (repli) sur toutes les requêtes modifiant l'état. Utiliser uniquement la correspondance exacte — une vérification startsWith permet à https://cible.com.evil.com de passer.
def valider_origin(request: Request, origin_attendu: str) -> None:
origin = request.headers.get("Origin") or ""
referer = request.headers.get("Referer") or ""
source = origin or referer
# Correspondance exacte puis slash — prévient le contournement target.com.evil.com
if not (source == origin_attendu or source.startswith(origin_attendu + "/")):
raise HTTPException(status_code=403, detail="Origin invalide")
# Rejeter explicitement l'origin null (iframes sandbox)
if origin == "null":
raise HTTPException(status_code=403, detail="Origin null rejeté")Qu'est-ce qu'une attaque CSRF ? Le Cross-Site Request Forgery trompe le navigateur d'un utilisateur authentifié pour qu'il envoie une requête d'état vers une application de confiance à son insu. Le navigateur joint automatiquement les cookies de session à chaque requête — la page de l'attaquant exploite cela pour forger des actions privilégiées. Classifié CWE-352.
Quelle est la différence entre CSRF et XSS ? Le XSS injecte un script qui s'exécute dans l'origine du navigateur de la victime et peut lire des données, voler des cookies et appeler des APIs. Le CSRF forge des requêtes sortantes sans lire les réponses — il exécute des actions. Le XSS peut contourner les tokens CSRF en les lisant depuis le DOM via XMLHttpRequest.
SameSite=Lax protège-t-il contre le CSRF ? Partiellement. Il bloque les soumissions de formulaires POST intersites mais autorise les navigations GET intersites et la fenêtre de grâce Chrome Lax+POST de 120 secondes. Combiner SameSite=Lax avec un token CSRF.
Qu'est-ce que le token synchroniseur ? Un token aléatoire cryptographiquement lié à la session intégré dans chaque formulaire modifiant l'état. Le serveur le valide à la soumission. Les attaquants ne peuvent pas lire le token en intersite, rendant les formulaires forgés invalides.
Une API JSON est-elle immunisée contre le CSRF ?
Non. Les formulaires enctype="text/plain" créent un corps JSON envoyé avec Content-Type: text/plain — une requête CORS simple qui ne déclenche aucun preflight. Les serveurs qui analysent le corps JSON sans valider le Content-Type sont pleinement exploitables. CVE-2024-4994 (GitLab GraphQL, CVSS 8.1) le démontre.
Qu'est-ce que le Login CSRF ? Une attaque qui force la victime à s'authentifier en tant que compte de l'attaquant sur un endpoint pré-authentification (aucune session, aucun token). La victime stocke ensuite des données dans le contexte du compte de l'attaquant.
Qu'est-ce que la fenêtre de 2 minutes Lax ? La période de grâce Lax+POST de Chrome autorise les POST intersites si le cookie de session a été émis dans les 120 dernières secondes. Un attaquant force une nouvelle authentification via une redirection de déconnexion puis déclenche immédiatement le payload CSRF dans cette fenêtre.
Le CSRF peut-il contourner l'authentification à deux facteurs ? Non. Le CSRF opère sur une session authentifiée existante dans laquelle l'authentification à deux facteurs a déjà été validée. Il ne peut pas contourner l'étape d'authentification elle-même.
Le Cross-Site Request Forgery (CSRF) trompe le navigateur d'un utilisateur authentifié pour qu'il envoie une requête modifiant l'état d'une application de confiance, à l'insu de l'utilisateur. Le navigateur joint automatiquement les cookies de session à chaque requête vers le domaine cible — la page de l'attaquant exploite ce mécanisme pour forger des actions privilégiées. Classifié CWE-352.
Le XSS injecte et exécute des scripts dans le navigateur de la victime dans le contexte de l'origine cible — il lit des données et peut voler des cookies. Le CSRF forge des requêtes sortantes en exploitant l'envoi automatique des cookies — il exécute des actions sans lire les réponses. Le XSS peut contourner les tokens CSRF en les lisant via XMLHttpRequest.
Le CSRF est une attaque côté client : le navigateur de la victime est instrumentalisé pour envoyer une requête à un serveur sur lequel la victime est authentifiée. Le SSRF est une attaque côté serveur : l'attaquant force le serveur lui-même à effectuer des requêtes vers une infrastructure interne ou externe. Le CSRF nécessite une interaction utilisateur ; le SSRF non.
Partiellement. SameSite=Lax bloque les soumissions de formulaires POST intersites et les requêtes de sous-ressources, mais autorise les navigations GET de premier niveau. Les endpoints GET qui modifient l'état restent vulnérables. La fenêtre de grâce Chrome de 120 secondes (Lax+POST) autorise les POST intersites si le cookie de session a été émis dans les 2 dernières minutes. SameSite=Lax doit être combiné avec un token CSRF pour une protection complète.
Chrome applique le comportement SameSite=Lax aux cookies qui omettent l'attribut SameSite, mais accorde une période de grâce de 120 secondes après l'émission du cookie durant laquelle les requêtes POST intersites sont autorisées. Un attaquant qui force une nouvelle authentification (via une redirection de déconnexion) puis soumet le payload CSRF dans les 2 minutes peut contourner la protection SameSite=Lax sur les cookies de session nouvellement émis.
Le token synchroniseur génère côté serveur un token aléatoire cryptographiquement lié à la session (minimum 128 bits via un CSPRNG) et l'intègre dans chaque formulaire modifiant l'état en tant que champ caché. Le serveur valide le token à la soumission. La politique de même origine empêche l'attaquant de lire le token en intersite, rendant impossible la forge d'une requête valide.
Ce mécanisme définit le token CSRF à la fois comme cookie et comme paramètre de requête (ou en-tête). Le serveur vérifie qu'ils correspondent. La politique de même origine empêche les scripts intersites de lire les cookies. La variante naïve (sans signature HMAC) est vulnérable à l'injection de cookie depuis un sous-domaine compromis — utiliser HMAC-SHA256 lié à l'identifiant de session.
Non. Les APIs JSON étaient supposées sûres parce que application/json déclenche un preflight CORS. Cependant, un formulaire avec enctype=text/plain crée un corps JSON valide envoyé avec Content-Type: text/plain — un type « simple » CORS qui ne déclenche aucun preflight. Si le serveur analyse le corps JSON quel que soit le Content-Type, l'attaque réussit. CVE-2024-4994 (GitLab GraphQL, CVSS 8.1) exploite exactement ce mécanisme.
Le Login CSRF force la victime à s'authentifier en tant que compte de l'attaquant avant qu'aucune session n'existe. Comme l'état pré-authentification n'a pas de token CSRF à protéger, le formulaire de connexion est généralement non protégé. La victime interagit ensuite avec l'application — saisit des données de paiement, télécharge des documents — tout étant stocké dans le compte de l'attaquant.
Le paramètre state OAuth 2.0 (RFC 6749 §10.12) est un token CSRF par requête liant la demande d'autorisation à la session de l'utilisateur. Si le state est absent ou statique, un attaquant forge l'URL de callback avec son propre code d'autorisation — liant son compte social au compte de la victime (prise de contrôle de compte). RFC 9700 (OAuth 2.0 Security BCP, janvier 2025) impose une liaison cryptographique du state.
Le CSRF GraphQL exploite le fait que GraphQL accepte des mutations via des requêtes GET (aucun preflight), via application/x-www-form-urlencoded (aucun preflight), et parfois via text/plain. CVE-2024-4994 (GitLab, CVSS 8.1) et CVE-2025-68604 (WPGraphQL, CVSS 5.4) illustrent des cas réels de CSRF GraphQL. Apollo Server exige désormais l'en-tête Apollo-Require-Preflight par défaut.
Le CSRF ne contourne pas la 2FA directement — il opère sur une session authentifiée existante où la 2FA a déjà été complétée. Si la victime est déjà connectée et a passé la 2FA, le CSRF peut forger toute action que la victime est autorisée à effectuer dans cette session. La 2FA empêche les connexions non autorisées mais ne protège pas contre le CSRF sur les endpoints authentifiés.
1) Identifier tous les endpoints qui modifient l'état (POST/PUT/PATCH/DELETE et tout GET modifiant des données). 2) Dans Burp Suite, clic droit sur la requête → Engagement tools → Generate CSRF PoC. 3) Tester les contournements du token : supprimer le champ token, soumettre un token vide, soumettre un token aléatoire, soumettre le token valide d'un autre utilisateur. 4) Tester le contournement Content-Type : changer application/json en text/plain. 5) Vérifier les mutations GraphQL via GET et les corps form-encoded.
Les navigateurs modernes attachent les en-têtes Sec-Fetch-Site, Sec-Fetch-Mode et Sec-Fetch-Dest à chaque requête. La politique d'isolation des ressources côté serveur utilise Sec-Fetch-Site: cross-site pour détecter et rejeter les requêtes cross-origin forgées avant qu'elles n'atteignent la logique applicative. La couverture est de 98%+ au niveau mondial en 2024-2025. Pour les 2% restants, la validation des en-têtes Origin/Referer fournit un mécanisme de repli.
Le CVSS CSRF varie selon l'impact. CSRF typique sur un endpoint de données utilisateur : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N = 6.5 High. CSRF → changement de mot de passe/email (chemin de prise de contrôle de compte) : ~8.1 High. CSRF → création de compte admin ou configuration critique : ~9.0 Critical. CVE-2024-20252 (Cisco Expressway) a reçu 9.6 Critical pour un CSRF permettant des modifications de configuration système au niveau administrateur.