# SameSite contre les tokens CSRF, faut-il garder le token (/fr/blog/samesite-vs-csrf-tokens)



Non. `SameSite` ne remplace pas les tokens CSRF. L'attribut de cookie est une
vraie défense en profondeur contre le cross-site request forgery, mais il est
limité au site, le défaut du navigateur n'est pas appliqué partout, et son mode
assoupli laisse encore passer certaines requêtes cross-site. La spécification
des cookies elle-même dit que l'application de Lax n'est pas une défense
complète contre le CSRF et renvoie les développeurs vers un mécanisme de gestion
de session pour une couverture totale. Posez `SameSite` explicitement et gardez
vos tokens CSRF. Cet article explique ce que chaque couche bloque, les quatre
contournements documentés de `SameSite`, et pourquoi les deux défenses couvrent
mutuellement leurs failles.

## Ce qu'est le CSRF, et pourquoi les cookies le rendent possible [#ce-quest-le-csrf-et-pourquoi-les-cookies-le-rendent-possible]

Le cross-site request forgery (CSRF) est une attaque où une page que l'attaquant
contrôle fait envoyer par le navigateur de la victime une requête qui change l'état
vers votre site. La victime est déjà connectée, donc la requête transporte sa session
et le serveur agit dessus : virer de l'argent, changer un e-mail, supprimer un compte.

La raison pour laquelle cela fonctionne, c'est que le navigateur attache vos cookies à
une requête automatiquement, selon la destination de la requête, pas selon sa
provenance. La page d'un attaquant ne peut pas lire votre cookie de session, mais elle
peut déclencher une requête vers votre site (un auto-envoi de formulaire, un chargement
d'image, un `fetch`), et le navigateur inclut obligeamment le cookie de session quand
même. Cet attachement automatique est toute la vulnérabilité. Les deux défenses
ci-dessous existent pour la briser.

## Ce que SameSite bloque, et ce que Lax laisse encore passer [#ce-que-samesite-bloque-et-ce-que-lax-laisse-encore-passer]

`SameSite` est un [attribut Set-Cookie](/fr/docs/web-security/security-headers/cookie-security)
qui indique au navigateur quand un cookie peut accompagner des requêtes cross-site. Il
a trois valeurs.

* `SameSite=Strict` : le cookie n'est envoyé que sur des requêtes same-site. Une
  requête provenant de tout autre site ne le transporte jamais, y compris un clic
  de lien normal depuis un e-mail ou un autre site. La session est intacte, mais
  cette première navigation arrive sans le cookie, donc la page s'affiche comme si
  l'utilisateur était déconnecté.
* `SameSite=Lax` : le cookie est envoyé same-site, plus sur les navigations de premier
  niveau cross-site qui utilisent une méthode sûre (un clic de lien GET ou un
  formulaire GET). Une navigation de premier niveau est celle qui change la barre
  d'adresse, donc un `fetch` cross-site, un `<img>`, un `<script>` ou une requête
  `<iframe>` ne transporte jamais le cookie.
* `SameSite=None` : le cookie est envoyé sur chaque requête, cross-site incluse. La
  spécification des cookies exige que `None` soit accompagné de `Secure` ; Chromium
  et Firefox rejettent un cookie `None` sans lui, tandis que Safari l'accepte
  actuellement.

Lax stoppe le vecteur CSRF le plus courant, le POST cross-site caché, parce qu'un POST
cross-site n'est pas une navigation de premier niveau sûre. Ce que Lax laisse encore
passer, c'est le GET de premier niveau cross-site. Si une requête GET sur votre site
change l'état (elle ne le devrait pas, mais beaucoup le font), Lax n'empêchera pas un
attaquant de naviguer le navigateur de la victime vers elle.

Notez aussi ce que `SameSite` ne touche jamais : les requêtes same-site. Les trois
valeurs ne gouvernent que l'envoi cross-site. Toute requête déjà same-site, y compris
un `fetch` initié en JavaScript depuis n'importe quelle page de votre site, transporte
le cookie en entier. Cela compte plus bas.

## Les failles qui gardent les tokens nécessaires [#les-failles-qui-gardent-les-tokens-nécessaires]

`SameSite` rétrécit la surface d'attaque. Il ne la referme pas, pour quatre raisons
documentées. Elles correspondent aux classes de contournement que PortSwigger
catalogue pour les restrictions `SameSite`.

**SameSite est limité au site, pas à l'origine.** Un « site » dans la spécification
des cookies est le schéma plus le domaine enregistrable, donc `https://a.example.com`
et `https://b.example.com` sont le même site, tandis que `http://example.com` et
`https://example.com` ne le sont pas. Un cookie posé avec n'importe quelle valeur
`SameSite` est envoyé sur les requêtes provenant de n'importe quel sous-domaine frère.
La formulation de PortSwigger mérite d'être retenue : une requête peut rester
same-site même si elle est émise cross-origin. Un bug XSS ou une redirection ouverte
sur n'importe quel sous-domaine de votre domaine enregistrable devient un gadget
same-site ; les requêtes lancées depuis lui transportent tous les cookies du site, et
`SameSite` n'apporte rien. Un token CSRF exige toujours que l'attaquant obtienne la
valeur du token. (Un XSS complet sur la même origine que le formulaire peut en général
lire le token aussi, donc les tokens atténuent surtout le cas du sous-domaine frère
plutôt que le XSS same-origin ; le XSS demande ses propres défenses, à commencer par
une [Content Security Policy](/fr/docs/web-security/policies/content-security-policy).)

**Le défaut du navigateur n'est pas appliqué partout.** Les navigateurs basés sur
Chromium traitent un cookie sans attribut `SameSite` comme Lax. Firefox n'a livré ce
comportement que derrière un flag, jamais dans une version stable, et a désactivé même
son expérimentation Nightly en 2023. Safari n'applique aucun défaut. Dans ces
navigateurs, un cookie de session sans attribut accompagne chaque requête cross-site,
exactement le comportement d'avant 2020. Se fier au défaut, c'est se fier au
navigateur que votre utilisateur se trouve exécuter. Posez l'attribut vous-même.

**Les cookies sans attribut bénéficient dans Chromium d'une exception de deux minutes
pour les POST cross-site.** Quand Chromium applique le défaut Lax à un cookie, il
envoie quand même ce cookie sur les requêtes POST de premier niveau cross-site pendant
les deux premières minutes après la création du cookie, une intervention annoncée
comme temporaire en 2019 et toujours présente dans le source de Chromium en 2026.
PortSwigger documente des attaques qui rouvrent la fenêtre en forçant le navigateur de
la victime à récupérer un cookie de session frais, puis en tirant le POST de premier
niveau forgé dans les 120 secondes. L'exception ne s'applique qu'aux cookies sans
attribut ; un cookie posé avec un `SameSite=Lax` explicite ne l'obtient jamais. Une
raison de plus de poser la valeur plutôt que de s'appuyer sur le défaut.

**Un GET qui change l'état reste exposé sous Lax.** Lax permet par conception les
navigations GET de premier niveau cross-site. Cela couvre non seulement les endpoints
qui détournent GET, mais aussi les frameworks qui honorent un paramètre de
substitution de méthode (le `_method` de Symfony, par exemple), qui laissent un GET
permis par Lax atteindre une route POST côté serveur. Une vérification de token ne se
soucie pas de la méthode, donc elle couvre les deux cas.

## Pourquoi HttpOnly n'aide pas contre le CSRF [#pourquoi-httponly-naide-pas-contre-le-csrf]

`HttpOnly` vaut la peine d'être posé, mais il résout un problème différent. Il empêche
JavaScript de lire le cookie (`document.cookie`), ce qui bloque un payload de
cross-site scripting (XSS) qui volerait la valeur de session. Il ne fait rien contre
le CSRF, parce que le CSRF n'a jamais besoin de lire le cookie. Le navigateur attache
toujours un cookie `HttpOnly` à la requête forgée automatiquement, ce qui est
exactement le comportement dont l'attaque abuse.

## SameSite vs tokens CSRF, ce que chaque couche bloque [#samesite-vs-tokens-csrf-ce-que-chaque-couche-bloque]

Les tokens CSRF utilisent une valeur que l'attaquant ne peut pas fournir. Le serveur
génère un token imprévisible, l'intègre dans la page ou le formulaire, et rejette
toute requête qui change l'état sans le renvoyer. C'est la recommandation principale
d'OWASP pour les applications avec état (le pattern du token synchronisé), avec
`SameSite` traité comme une couche de défense en profondeur par-dessus, pas comme un
remplaçant. La page cross-site d'un attaquant ne peut pas lire le token (la
same-origin policy le bloque), donc elle ne peut pas forger une requête qui passe la
vérification.

| Chemin d'attaque                                                      | Couche SameSite                                                 | Couche token CSRF            |
| --------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------- |
| POST cross-site depuis un formulaire caché                            | Bloqué par Lax et Strict                                        | Bloqué (pas de token valide) |
| Requête de sous-ressource cross-site (`fetch`, `img`, `script`)       | Bloqué par Lax et Strict (pas une navigation de premier niveau) | Bloqué (pas de token valide) |
| GET de premier niveau cross-site qui change l'état                    | Autorisé sous Lax                                               | Bloqué (pas de token valide) |
| Substitution de méthode (`_method`) derrière un GET de premier niveau | Autorisé sous Lax                                               | Bloqué (pas de token valide) |
| Requête forgée depuis un sous-domaine frère                           | Non bloqué (même site)                                          | Bloqué (pas de token valide) |
| Utilisateur sur Safari ou Firefox avec un cookie sans attribut        | Non bloqué (pas de défaut Lax)                                  | Bloqué (pas de token valide) |
| Cookie sans attribut dans la fenêtre POST de deux minutes de Chromium | Non bloqué                                                      | Bloqué (pas de token valide) |
| Vol de cookie via XSS                                                 | Ce n'est pas son rôle                                           | Ce n'est pas son rôle        |

Le schéma est constant : `SameSite` est un bon mur extérieur que certaines requêtes
contournent, et le token est la vérification qui ne dépend ni de l'origine de la
requête, ni de la méthode de requête, ni du comportement du navigateur.

## Posez le cookie, et gardez le token [#posez-le-cookie-et-gardez-le-token]

Posez `SameSite` explicitement sur votre cookie de session, aux côtés de `Secure` et
`HttpOnly`, en utilisant le préfixe host-only `__Host-` pour un identifiant de session.

```http
Set-Cookie: __Host-session=8f3k...; Secure; HttpOnly; SameSite=Lax; Path=/
```

Utilisez `Strict` si arriver déconnecté après un lien externe est acceptable ;
utilisez `Lax` quand vous avez besoin que les liens entrants gardent la session. Puis
gardez un token sur chaque requête qui change l'état. Le token vit dans le formulaire
ou un header de requête, et le serveur le valide côté serveur.

```html
<form method="POST" action="/account/email">
  <input type="hidden" name="csrf_token" value="{{ server_generated_token }}">
  <input type="email" name="email">
  <button type="submit">Save</button>
</form>
```

## Recommandation [#recommandation]

Posez `SameSite` explicitement (`Strict` de préférence, `Lax` quand vous avez besoin
que les GET de premier niveau cross-site gardent la session) et ne vous fiez jamais au
défaut du navigateur. Posez aussi `Secure` et `HttpOnly`, et préférez le préfixe
`__Host-` pour les identifiants de session. Par-dessus, gardez des tokens CSRF sur
chaque endpoint qui change l'état, ce qui est le socle OWASP. `SameSite` rétrécit
l'attaque, le token referme les cas qu'il laisse ouverts, et ensemble ils couvrent les
failles l'un de l'autre. Vérifiez les attributs `Set-Cookie` que vous déployez
réellement avec le [scanner de headers de sécurité](/tools/security-headers) gratuit de
CentralCSP, qui rapporte les flags `SameSite`, `Secure` et `HttpOnly` sur chaque cookie
qu'une page pose.

## FAQ [#faq]

### SameSite suffit-il à empêcher le CSRF ? [#samesite-suffit-il-à-empêcher-le-csrf-]

Non. OWASP classe `SameSite` comme un contrôle de défense en profondeur qui ne
remplace pas une défense CSRF dédiée, et la spécification des cookies elle-même dit
que l'application de Lax n'est pas une défense complète contre le CSRF en tant que
catégorie. Il est limité au site (les sous-domaines frères le contournent), pas
appliqué par défaut dans Safari ni dans Firefox stable, et Lax permet encore les GET
de premier niveau cross-site.

### Ai-je encore besoin de tokens CSRF si je pose SameSite Lax ? [#ai-je-encore-besoin-de-tokens-csrf-si-je-pose-samesite-lax-]

Oui, pour presque toutes les applications. La recommandation principale d'OWASP pour
les applications avec état est le pattern du token synchronisé, avec `SameSite` en
couche par-dessus. Les tokens ne dépendent ni des défauts du navigateur, ni du
périmètre de site, ni de la méthode de requête, donc ils couvrent chaque faille que
Lax laisse.

### SameSite Lax ou Strict, lequel vaut mieux contre le CSRF ? [#samesite-lax-ou-strict-lequel-vaut-mieux-contre-le-csrf-]

Strict n'envoie jamais le cookie cross-site, donc il bloque aussi le cas du GET qui
change l'état, mais les utilisateurs arrivant d'un lien externe atterrissent sur une
vue déconnectée. Lax garde les liens entrants fonctionnels et laisse forgeable le GET
qui change l'état (ou une substitution de méthode derrière lui). Aucune des deux
valeurs ne protège contre un attaquant capable d'émettre des requêtes same-site.

### Quels sont les contournements connus de SameSite ? [#quels-sont-les-contournements-connus-de-samesite-]

Quatre classes documentées. Les GET de premier niveau cross-site sous Lax, y compris
les substitutions de méthode des frameworks. La fenêtre de deux minutes de Chromium
pour les POST cross-site avec des cookies sans attribut `SameSite`. Les gadgets
same-site, forger depuis un sous-domaine frère via un XSS ou une redirection ouverte.
Et les navigateurs sans défaut Lax (Safari, Firefox stable), qui envoient un cookie
sans attribut sur chaque requête cross-site.

## Lectures liées [#lectures-liées]

* [Cookie security, les attributs Set-Cookie](/fr/docs/web-security/security-headers/cookie-security),
  la référence pour `Secure`, `HttpOnly`, `SameSite`, et les préfixes de cookie.
* [X-Frame-Options contre frame-ancestors](/fr/blog/x-frame-options-vs-frame-ancestors),
  l'autre contrôle du navigateur pour une attaque cross-site (le clickjacking).
* [Comment améliorer votre note de headers de sécurité](/fr/blog/improve-security-headers-grade),
  la liste de correctifs pour l'ensemble des headers de réponse, cookies compris.
* [no-cache contre no-store](/fr/blog/no-cache-vs-no-store), garder hors des
  caches partagés les pages qui portent la session.

## Sources [#sources]

* [rfc6265bis (Cookies, HTTP State Management Mechanism)](https://httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html), la sémantique de SameSite, la définition de site, le Lax qui laisse passer les navigations non sûres, et la règle None-exige-Secure.
* [Chromium SameSite updates](https://www.chromium.org/updates/same-site/), le déploiement du Lax par défaut et l'intervention Lax+POST de deux minutes.
* [Chromium cookie\_constants.h](https://github.com/chromium/chromium/blob/main/net/cookies/cookie_constants.h), la tolérance de deux minutes toujours présente dans le source.
* [Référence MDN Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie), `SameSite`, `HttpOnly` et le préfixe `__Host-`.
* [browser-compat-data de MDN pour Set-Cookie](https://raw.githubusercontent.com/mdn/browser-compat-data/main/http/headers/Set-Cookie.json), le défaut Lax par navigateur et le support de None-exige-Secure.
* [Bug Mozilla 1812297](https://bugzilla.mozilla.org/show_bug.cgi?id=1812297), Firefox n'a jamais livré le Lax par défaut dans une version stable.
* [OWASP CSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html), le pattern du token synchronisé et la position de défense en profondeur.
* [OWASP Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html), le socle du cookie de session.
* [PortSwigger, contourner les restrictions SameSite](https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions), les techniques de contournement documentées.
