CentralCSP
Headers de sécurité

Sécurité des cookies

Les attributs Set-Cookie qui protègent les sessions, Secure, HttpOnly, SameSite et les préfixes __Host- et __Secure-, expliqués simplement.

Dernière mise à jour:

Les cookies portent la session. Après la connexion, le serveur pose un cookie, le navigateur l'attache à chaque requête correspondante, et quiconque détient cette valeur de cookie est connecté en tant que l'utilisateur. Il n'existe pas de header « sécurité des cookies » unique ; la protection tient dans une poignée d'attributs sur le header de réponse Set-Cookie lui-même.

Ces attributs (Secure, HttpOnly, SameSite, les préfixes de nom __Host- et __Secure-, et Partitioned) décident si le cookie peut être lu sur le réseau, volé par un script injecté, rejoué par une requête cross-site ou planté par un sous-domaine. Chacun tient en quelques caractères sur la même ligne, et les omettre est ce qui rend une session dérobable.

Voici un cookie de session durci tel que vous le déploieriez :

Set-Cookie: __Host-session=<value>; Secure; HttpOnly; SameSite=Lax; Path=/

Valeurs et ce que fait chacune

AttributStatutDescription
Secure✅ BonN'envoyer le cookie qu'en HTTPS, jamais sur une requête HTTP en clair. Obligatoire pour les cookies de session.
HttpOnly✅ BonCache le cookie à JavaScript. Les scripts ne peuvent ni lire ni exfiltrer la valeur.
SameSite=Strict✅ BonN'envoie que sur les requêtes same-site. La plus forte protection CSRF, mais abandonne la session sur les liens cross-site entrants.
SameSite=Lax✅ BonRequêtes same-site plus navigations de premier niveau cross-site avec méthodes sûres (clics sur un lien). Le défaut pratique pour les sessions.
SameSite=None❌ RisquéEnvoie sur toutes les requêtes cross-site. Légitime uniquement pour les intégrations cross-site, exige Secure, et Partitioned convient mieux à l'état d'un widget.
(pas de SameSite)❌ RisquéLe défaut varie selon le navigateur, et le Lax par défaut de Chrome garde une exception de 2 minutes pour les POST cross-site. Réglez toujours l'attribut explicitement.
préfixe __Host-✅ BonLe navigateur rejette le cookie sauf s'il a Secure, aucun Domain et Path=/. Le lie à un seul hôte exactement.
préfixe __Secure-✅ BonLe navigateur rejette le cookie sauf s'il est posé avec Secure depuis une origine sécurisée.
préfixes __Http- / __Host-Http-🧪 ExpérimentalExigent en plus HttpOnly, prouvant que le cookie vient d'un header et jamais d'un script. Dans Chrome et Firefox, pas Safari, en cours de standardisation.
Partitioned✅ BonStocke un cookie cross-site par site de premier niveau (CHIPS) pour qu'une intégration garde son état sans devenir un identifiant cross-site. Baseline largement disponible selon MDN.

Secure

Secure indique au navigateur de n'attacher le cookie qu'aux requêtes HTTPS, de sorte qu'il ne traverse jamais le réseau en clair (les navigateurs lèvent l'exigence sur localhost). Il restreint la transmission, pas la pose : un attaquant qui répond à une requête HTTP en clair peut toujours poser des cookies pour l'hôte, même si un Set-Cookie non sécurisé ne peut pas écraser un cookie Secure existant du même nom. Associez-le à Strict-Transport-Security pour que la requête non sécurisée ne quitte jamais la machine en premier lieu.

HttpOnly

HttpOnly cache le cookie à document.cookie et à l' API Cookie Store, de sorte qu'un script s'exécutant dans la page ne peut pas lire la valeur. Le navigateur attache toujours le cookie aux requêtes que les scripts déclenchent, ce qui signifie qu'un code injecté peut agir comme l'utilisateur sur place ; ce que HttpOnly empêche, c'est de copier la session vers l'extérieur pour un usage ultérieur. Posez-le sur chaque cookie dont JavaScript n'a pas besoin, ce qui, pour les cookies de session, veut dire tous.

SameSite

SameSite contrôle si le cookie accompagne les requêtes cross-site. Strict ne l'envoie que sur les requêtes same-site. Lax ajoute les navigations de premier niveau cross-site avec méthodes sûres (un lien cliqué, un formulaire GET), mais jamais les sous-ressources cross-site comme les appels fetch(), les images ou les iframes. None l'envoie partout et est rejeté d'emblée sauf si Secure est aussi présent. Ne vous fiez jamais au défaut du navigateur ; il diffère selon le navigateur (voir la section prise en charge par les navigateurs plus bas).

Préfixes de cookies

Les préfixes sont des conventions de nommage que le navigateur fait respecter au moment où le cookie est posé. Un cookie __Secure- doit porter Secure et venir d'une origine sécurisée. Un cookie __Host- doit en outre n'avoir aucun attribut Domain et un Path=/, ce qui le lie à un seul hôte exactement : les sous-domaines ne peuvent ni le poser ni le masquer, ce qui ferme la faille de cookie tossing décrite plus bas. Les préfixes plus récents __Http- et __Host-Http- (pris en charge dans Chrome et Firefox, en cours de standardisation) exigent en plus HttpOnly, de sorte que le nom lui-même prouve que le cookie a été posé par un header et jamais par un script.

Partitioned

Partitioned fait entrer un cookie cross-site dans un stockage indexé par le site de premier niveau (la proposition CHIPS), de sorte qu'un widget intégré garde un état distinct sur chaque site qui l'intègre, au lieu d'un identifiant unique qui suit l'utilisateur à travers le web. Il exige Secure et s'utilise avec SameSite=None, et MDN recommande de le poser avec le préfixe __Host-. Il a atteint Baseline lorsque Firefox et Safari ont rejoint Chrome dans sa prise en charge.

Contre quoi cela protège

  • L'interception de session en transit. Secure garde le cookie hors des sauts HTTP en clair, où un espion passif sur le chemin pourrait le copier et rejouer la session. OWASP le considère comme obligatoire pour les cookies de session.
  • Le vol de cookie par XSS. HttpOnly empêche un script injecté de lire la valeur de session et de l'envoyer au serveur d'un attaquant. Il n'empêche pas le script d'utiliser la session sur place, car le navigateur attache toujours le cookie aux requêtes que fait la page ; une politique de sécurité du contenu (CSP) traite l'injection elle-même.
  • La falsification de requête cross-site, en défense en profondeur. SameSite retire le cookie de la plupart des requêtes cross-site, ce dont abuse le CSRF. Il réduit l'attaque, il ne remplace pas les jetons CSRF (voir les pièges plus bas pour comprendre pourquoi, et SameSite contre les tokens CSRF pour la comparaison complète).
  • Le cookie tossing depuis les sous-domaines. N'importe quel sous-domaine peut normalement planter un cookie auquel l'application parente fera confiance, un problème d'intégrité faible que rfc6265bis documente et que la recherche OAuth de Snyk a transformé en véritables attaques de fixation de session. Le préfixe __Host- le ferme.

Risques sans ces attributs

Un cookie de session sans attribut est exposé sur tous les fronts. Il voyage en clair sur toute requête HTTP en clair vers l'hôte, où quiconque se trouve sur le chemin réseau peut le copier et détourner la session. Tout script injecté dans la page peut le lire via document.cookie et l'exfiltrer. Chaque requête cross-site le transporte, ce qui est exactement l'ouverture dont le CSRF a besoin. Et n'importe quel sous-domaine, y compris un oublié ou vulnérable, peut planter un cookie sosie que l'application principale acceptera, fixant la victime dans une session contrôlée par l'attaquant.

Risques et pièges à l'usage

  • SameSite=None sans Secure n'aboutit jamais. Chrome, Edge et le Firefox actuel refusent silencieusement de stocker le cookie. C'est la première cause du « mon cookie cross-site a disparu ».
  • L'exception Lax+POST de 2 minutes de Chrome. Les cookies sans attribut SameSite sont encore envoyés sur les POST de premier niveau cross-site pendant 2 minutes après leur création, une exception toujours appliquée par Chrome à la mi-2026 sans date de retrait annoncée. Un SameSite=Lax explicite n'obtient jamais l'exception, une raison de plus de régler l'attribut vous-même.
  • SameSite est à portée de site, pas d'origine. Les sous-domaines frères comptent comme same-site, donc l'attribut n'offre aucune protection contre un sous-domaine compromis. Et http:// face à https:// sur le même hôte ne compte comme cross-site que là où le same-site avec schéma est livré (Chrome, pas Safari, Firefox derrière un flag).
  • Domain élargit l'exposition. Domain=example.com envoie le cookie à chaque sous-domaine et laisse chaque sous-domaine l'écraser. Omettez-le pour que le cookie reste réservé à l'hôte ; __Host- impose exactement cela.
  • Les préfixes ne sont qu'un durcissement côté client. Un navigateur qui ne connaît pas un préfixe stocke le cookie sans aucune vérification, et PortSwigger a documenté des cas limites d'analyse qui contournent les vérifications dans les navigateurs qui les connaissent. Ils durcissent une conception, ils ne remplacent pas les contrôles de session côté serveur.
  • Strict abandonne les sessions sur les liens entrants. Un cookie de session SameSite=Strict n'est pas envoyé quand l'utilisateur arrive depuis un autre site, si bien que les visiteurs qui suivent un lien depuis un e-mail ou des résultats de recherche paraissent déconnectés jusqu'à ce qu'ils naviguent de nouveau.

Comment le mettre en place

  1. Nommez le cookie de session avec le préfixe __Host- et posez-y Secure, HttpOnly et SameSite, Strict si vos utilisateurs n'entrent jamais dans l'application par des liens cross-site, sinon Lax.
  2. Supprimez l'attribut Domain et mettez Path=/ ; __Host- exige les deux, et les cookies réservés à l'hôte sont tout l'intérêt.
  3. Omettez Expires et Max-Age pour que le cookie de session soit non persistant et meure avec la session du navigateur.
  4. Testez les parcours d'entrée cross-site (liens depuis un e-mail, redirections OAuth, tout contenu intégré) avant de vous arrêter sur Strict, et donnez aux cookies d'intégration réellement cross-site SameSite=None; Secure; Partitioned plutôt que d'assouplir le cookie de session.
  5. Vérifiez les attributs Set-Cookie déployés, ainsi que le reste de vos headers de réponse, avec le scanner de headers de sécurité.

Recommandation

Posez chaque cookie de session avec le préfixe __Host- plus Secure, HttpOnly et une valeur SameSite explicite :

Set-Cookie: __Host-session=<value>; Secure; HttpOnly; SameSite=Lax; Path=/

Cela suit le cheat sheet OWASP sur la gestion des sessions : Secure et HttpOnly toujours, SameSite=Strict de préférence ou Lax là où les liens d'entrée cross-site doivent conserver la session, jamais le défaut du navigateur, et le préfixe __Host- sur les identifiants de session. Aucun attribut Domain, un Path restrictif, et pas d'Expires ni de Max-Age pour que le cookie ne persiste pas sur le disque.

Prise en charge par les navigateurs

Les attributs de base sont universels : Secure, HttpOnly, les trois valeurs SameSite et les préfixes __Secure- et __Host- fonctionnent dans tous les navigateurs actuels. Les différences portent sur les défauts et les attributs plus récents. Chrome et Edge traitent un cookie sans SameSite comme Lax (moins l'exception de 2 minutes sur les POST ci-dessus) ; Firefox n'a ce défaut que derrière un flag ; Safari ne l'applique jamais, si bien qu'un cookie sans attribut s'y comporte comme None. La règle selon laquelle SameSite=None exige Secure est appliquée par Chrome, Edge et le Firefox actuel, mais pas par Safari. Les préfixes __Http- et __Host-Http- sont récents dans Chrome et Firefox, sans prise en charge par Safari, et Partitioned est Baseline largement disponible dans Chrome, Firefox et Safari. Le paysage des cookies tiers s'est stabilisé en 2025 : Chrome a conservé les cookies tiers tout en gardant CHIPS, Safari les bloque avec Intelligent Tracking Prevention, et Firefox les partitionne avec Total Cookie Protection.

FAQ

SameSite remplace-t-il les jetons CSRF ?

Non. SameSite réduit le CSRF en défense en profondeur mais reste à portée de site et dépendant du navigateur ; gardez vos jetons CSRF. SameSite contre les tokens CSRF détaille les failles et les contournements documentés.

SameSite Strict ou Lax ?

Strict n'envoie le cookie que sur les requêtes same-site, donc il abandonne la session quand un utilisateur arrive par un lien cross-site entrant, et celui-ci paraît déconnecté jusqu'à ce qu'il navigue de nouveau. Lax conserve les navigations GET de premier niveau, donc les sessions survivent aux liens depuis un e-mail ou une recherche. OWASP préfère Strict ; Lax reste acceptable.

Il lui manque l'attribut Secure. Un cookie posé avec SameSite=None est rejeté d'emblée sauf si Secure est aussi présent, donc Chrome, Edge et le Firefox actuel refusent silencieusement de le stocker. Ajoutez Secure, et pour l'état d'un widget intégré, préférez SameSite=None; Secure; Partitioned afin que le cookie ne devienne pas un identifiant cross-site.

HttpOnly empêche-t-il le CSRF ?

Non. HttpOnly cache le cookie à document.cookie et à l'API Cookie Store, ce qui empêche un script de lire et d'exfiltrer la valeur. Il n'empêche pas le navigateur d'attacher le cookie aux requêtes cross-site, ce dont abuse justement le CSRF. Utilisez SameSite et des jetons CSRF pour cela.

Voir aussi

Sources

On this page