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
| Attribut | Statut | Description |
|---|---|---|
Secure | ✅ Bon | N'envoyer le cookie qu'en HTTPS, jamais sur une requête HTTP en clair. Obligatoire pour les cookies de session. |
HttpOnly | ✅ Bon | Cache le cookie à JavaScript. Les scripts ne peuvent ni lire ni exfiltrer la valeur. |
SameSite=Strict | ✅ Bon | N'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 | ✅ Bon | Requê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- | ✅ Bon | Le navigateur rejette le cookie sauf s'il a Secure, aucun Domain et Path=/. Le lie à un seul hôte exactement. |
préfixe __Secure- | ✅ Bon | Le navigateur rejette le cookie sauf s'il est posé avec Secure depuis une origine sécurisée. |
préfixes __Http- / __Host-Http- | 🧪 Expérimental | Exigent 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 | ✅ Bon | Stocke 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.
Securegarde 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.
HttpOnlyempê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.
SameSiteretire 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=NonesansSecuren'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
SameSitesont 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. UnSameSite=Laxexplicite n'obtient jamais l'exception, une raison de plus de régler l'attribut vous-même. SameSiteest à 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. Ethttp://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.comenvoie 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.
Strictabandonne les sessions sur les liens entrants. Un cookie de sessionSameSite=Strictn'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
- Nommez le cookie de session avec le préfixe
__Host-et posez-ySecure,HttpOnlyetSameSite,Strictsi vos utilisateurs n'entrent jamais dans l'application par des liens cross-site, sinonLax. - Supprimez l'attribut
Domainet mettezPath=/;__Host-exige les deux, et les cookies réservés à l'hôte sont tout l'intérêt. - Omettez
ExpiresetMax-Agepour que le cookie de session soit non persistant et meure avec la session du navigateur. - 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-siteSameSite=None; Secure; Partitionedplutôt que d'assouplir le cookie de session. - Vérifiez les attributs
Set-Cookiedé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.
Pourquoi mon cookie SameSite=None n'est-il pas posé ?
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
- Strict-Transport-Security,
le header qui rend l'attribut
Secureétanche en supprimant entièrement le saut HTTP en clair - Vue d'ensemble des headers de sécurité
- Cache-Control pour garder hors des caches partagés les réponses qui portent la session
- Scanner de headers de sécurité pour vérifier vos attributs de cookie et le reste de vos headers déployés
Sources
Strict-Transport-Security
Le header HSTS épingle HTTPS pour les prochaines visites, si bien que le navigateur ne retente jamais le HTTP en clair. Rôle et déploiement sûr.
X-Content-Type-Options
Le header nosniff empêche les navigateurs de deviner le Content-Type et ferme les attaques par MIME sniffing. Rôle et mise en place.