Date de publication du RFC : Avril 2011
Auteur(s) du RFC : A. Barth (UC Berkeley)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpstate
Première rédaction de cet article le 29 avril 2011
Dernière mise à jour le 30 avril 2011
Les cookies, ces célèbres gâteaux du Web ont déjà fait l'objet d'innombrables articles, par exemple pour critiquer les conséquences pour la vie privée d'un mécanisme qui permet de suivre l'utilisateur à la trace (voir par exemple l'introduction de la CNIL). Mais on connait moins leur spécification technique, ou plutôt le fait que, mal spécifiés dès le début, ces gâteaux ont été mis en œuvre de diverses manières et qu'il est très difficile de décrire intégralement leur fonctionnement. C'est la tâche de notre RFC 6265, d'être, normalement, la norme technique complète et faisant autorité sur les cookies, en capturant l'ensemble des points techniques sur les gâteaux en une seule norme. Les textes précédents étaient la description originale de Netscape (on notera avec ironie que cette spécification privée n'est plus accessible que via WebArchive), puis le RFC 2109, puis le RFC 2965, que notre RFC remplace et annule. Les documents précédents étaient loin de spécifier tous les aspects des petits gâteaux.
Le point de départ est que le protocole
HTTP, le plus utilisé du
Web est sans état. Lors
d'une visite à un site Web, l'utilisateur clique successivement sur
plusieurs liens, visite plusieurs pages, et chaque téléchargement
d'une page est une requête HTTP séparée des autres, sans rien qui
permette de les corréler (par exemple pour se souvenir des préférences
exprimées par
un utilisateur lors de l'arrivée sur la première page). Les gâteaux
visent à combler ce manque. La norme définit deux en-têtes HTTP,
Set-Cookie
qui permet au
serveur HTTP de déposer un gâteau sur
l'ordinateur de l'utilisateur et Cookie
qui
permet au client HTTP (typiquement le
navigateur) de renvoyer un gâteau préalablement
déposé. Lorsque le serveur reçoit le gâteau, il le compare à ceux
qu'il a envoyés et reconnait ainsi un utilisateur déjà vu. HTTP
devient alors un protocole avec état.
Ce n'est pas que les gâteaux soient une solution géniale : imposés par un acteur du Web particulier, Netscape, ils n'avaient pas fait l'objet d'un examen sérieux par une SDO et ont des tas d'inconvénients techniques et politiques. Néanmoins, comme ils sont largement utilisés dans le Web, il était préférable qu'une documentation sérieuse de leur syntaxe et de leur comportement existe, et c'est le rôle de ce RFC 6265.
La section 1 de ce RFC résume le principe des gâteaux. Un gâteau
inclus un certain nombre de métadonnées qui
permettent au client HTTP de savoir si et quand il doit retransmettre
le cookie. Par exemple, on peut spécifier la
période pendant laquelle le gâteau reste valable, les serveurs à qui
on doit le renvoyer, etc. Du fait de leur spécification hâtive et non
collective, les gâteaux ont bien des faiblesses techniques. Par
exemple, l'attribut secure
dans les métadonnées
ne fournit aucune garantie d'intégrité en dépit
de ce que son nom pourrait faire croire. D'autre part, les gâteaux ne
sont pas spécifiques à un port donné donc, si
plusieurs serveurs tournent sur des ports différents de la même
machine, ils peuvent se voler leurs gâteaux mutuellement. Mais, d'une
manière générale, ce RFC n'essaie pas de réparer la situation, mais de
documenter les gâteaux tels qu'ils sont réellement produits et
consommés sur le Web.
Bien, assez râlé, qui doit lire ce RFC ? Il y a deux audiences, les développeurs d'applications côté serveurs (ou, plus exactement, des bibliothèques qui sont utilisées par ces applications) qui produisent et consomment des gâteaux, et les développeurs des logiciels clients, notamment les navigateurs Web. Pour maximiser les chances d'interopérabilité, les serveurs devraient se montrer très prudents et ne produire que des gâteaux parfaitement conformes à la norme (section 4 du RFC, qui décrit le profil restreint), alors que les clients doivent être plus tolérants et accepter la grande variété des gâteaux actuellement fabriqués (section 5, sur le profil libéral), car tous les serveurs ne suivent pas le profil restreint.
Avant de lire le gros du RFC, un petit détour par la norme HTTP, le RFC 7230, peut être utile, puisque notre RFC en reprend le vocabulaire.
La section 3 décrit les généralités sur les gâteaux. Ceux-si sont
envoyés par un en-tête Set-Cookie
dans la
réponse HTTP, puis retournés dans un en-tête
Cookie
dans la requête
suivante. L'état est donc stocké chez le client mais, en pratique, la
taille des gâteaux étant limité, le serveur stocke l'essentiel de
l'état et le gâteau ne sert que d'index pour retrouver une session
particulière. Un serveur peut utiliser Set-Cookie
dans n'importe quelle réponse (y compris les erreurs comme 404) et le client doit le gérer (sauf pour
les réponses de type 1xx, qui sont seulement « pour
information »). Plusieurs gâteaux peuvent être envoyés, chacun dans un
en-tête Set-Cookie
différent (le client, lui,
peut utiliser un seul en-tête Cookie
pour envoyer
plusieurs gâteaux). Un exemple
classique est l'envoi d'un identificateur de session (ici, SID, pour
Session ID). Le serveur envoie :
Set-Cookie: SID=31d4d96e407aad42
et à la requête suivante, le client renverra :
Cookie: SID=31d4d96e407aad42
Le serveur peut réduire la portée du gâteau avec les attributs
Path
et Domain
. Ici, par
exemple, le gâteau doit être renvoyé pour tous les
URL dans example.com
:
Set-Cookie: SID=31d4d96e407aad42; Path=/; Domain=example.com
Ici, un serveur prudent utilise en plus les attributs
Secure
et HttpOnly
. En
outre, il envoie deux gâteaux, SID et lang (chacun étant un doublet nom-valeur) :
Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly Set-Cookie: lang=en-US; Path=/; Domain=example.com
Le client peut les renvoyer en un seul en-tête :
Cookie: SID=31d4d96e407aad42; lang=en-US
Ici, le serveur fixe une date d'expiration au gâteau :
Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT
Après ces exemples tirés du RFC, voici maintenant un exemple réel entre un site Web écrit en PHP et un navigateur. Le trafic a été capturé et formaté avec tshark :
# Le serveur envoie : Set-Cookie: PHPSESSID=euk3daoldp4s3j90uocetas3a0; path=/\r\n # Le client répond (d'autres gâteaux avaient été transmis) : Cookie: __utma=85646793.1868252067.1190397623.1190397623.1190397623.1; \ PHPSESSID=euk3daoldp4s3j90uocetas3a0; \ culture=fr\r\n
Autre façon de voir les gâteaux, se servir du client en ligne de commande curl :
% curl -v http://twitter.com/ > /dev/null ... < Set-Cookie: guest_id=130393513906128799; path=/; expires=Fri, 27 May 2011 20:12:19 GMT < Set-Cookie: _twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCPc8l5gvAToHaWQiJWY3NTg3YTU1YTgwOWE2%250AODcxMTA5M2NiNzNiOWMxODRmIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--dd45d4b08ccac6e6f832ccb70af983e16a12df43; domain=.twitter.com; path=/; HttpOnly
curl permet même de renvoyer ces gâteaux au serveur :
% curl --cookie _twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCPc8l5gvAToHaWQiJWY3NTg3YTU1YTgwOWE2%250AODcxMTA5M2NiNzNiOWMxODRmIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--dd45d4b08ccac6e6f832ccb70af983e16a12df43\;guest_id=130393513906128799 -v http://twitter.com/ > /dev/null ... > Cookie: _twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCPc8l5gvAToHaWQiJWY3NTg3YTU1YTgwOWE2%250AODcxMTA5M2NiNzNiOWMxODRmIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--dd45d4b08ccac6e6f832ccb70af983e16a12df43;guest_id=130393513906128799 ...
La section 4 définit rigoureusement les obligations d'un serveur sérieux, qui ne cherche pas à exploiter les possibilités de la norme à fond et qui veut être sûr que tous les clients pourront traiter correctement ses gâteaux. Sa section 4.1.1 définit la syntaxe formelle à suivre, en ABNF. Les valeurs qui ne sont pas réduites à un strict sous-ensemble de caractères sûrs, pour maximiser les chances d'être transmises sans accident, sont encodées en Base64. Les dates s'expriment avec le format du RFC 1123 (mais certains serveurs non compatibles avec ce profil envoient des dates sur deux chiffres...). Certains clients stockent les dates dans un entier de seulement 32 bits donc il est possible que les choses se passent mal à partir de 2038.
Le serveur peut ajouter des attributs aux gâteaux. Ainsi,
l'attribut Expires
(section 4.1.2.1) permet de
spécifier la date limite de consommation du gâteau (« À consommer avant le 29 avril
2011 ») et l'attribut Max-Age
indique la durée de
vie maximale (le RFC prévient que Max-Age
est
moins souvent mis en œuvre que Expires
par
les clients HTTP). Le gâteau peut durer moins longtemps que cela, par exemple
parce que le navigateur Web a décidé de faire de la place, ou bien
parce que l'utilisateur l'a détruit explicitement (chose que
permettent tous les bons navigateurs, vues les conséquences négatives des
gâteaux pour la vie privée).
Important pour la sécurité, l'attribut Domain
permet d'indiquer le domaine des serveurs à qui
le client doit renvoyer le gâteau. Attention, les sous-domaines de ce
domaine sont également acceptés. Si le serveur envoie
Domain=example.org
lors du
Set-Cookie
, le client renverra le gâteau à, par exemple,
www.example.org
et
mail.example.org
. (Et si un serveur distrait met
un gâteau avec Domain=com
? Il sera renvoyé à
tous les domains en .com
?
Cela dépend du navigateur ; beaucoup ignorent cet attribut
Domain
s'il correspond à un domaine
d'enregistrement comme .fr
ou .co.uk
mais il n'existe
pas de moyen officiel d'en connaître la liste donc ces navigateurs
dépendent de listes incomplètes comme http://publicsuffix.org
. Voir la section 5.3 pour une
discussion complète. À noter que Konqueror
utilise une méthode encore pire qui n'a jamais été corrigée.)
Le serveur peut restreindre encore plus l'utilisation du gâteau
qu'il envoie avec l'attribut Path
(section
4.1.2.4). Celui-ci indique quelle section du site Web a besoin du
gâteau. Si on met Path=/accounting
et que le
domaine est www.example.com
, seuls les URL
commençant par http://www.example.com/accounting
recevront le gâteau.
Enfin, deux autres attributs permettent d'améliorer la (faible)
sécurité des gâteaux, Secure
, qui impose au
client de ne renvoyer le gâteau qu'au dessus d'une connexion « sûre »
(typiquement HTTPS, pour éviter les attaques
par FireSheep) et HttpOnly
qui restreint le gâteau à ce protocole.
Une fois que le serveur a créé le gâteau et l'a envoyé avec les
attributs qu'il juge utile, il doit être prêt à recevoir des gâteaux
retransmis par le client. La section 4.2 décrit l'en-tête
Cookie
des requêtes HTTP, qui sert à cela. (Voir
les exemples plus haut.)
La section 4 décrivait un profil réduit des gâteaux. Un client HTTP sérieux qui veut pouvoir comprendre tous les serveurs existants dans la nature, y compris ceux qui ne s'en tiennent pas à ce profil réduit, doit, lui, lire la section 5, qui décrit l'intégralité du très compliqué comportement des gâteaux. Cette section reprend le plan de la 4, en détaillant toutes les horreurs qu'on peut rencontrer dans le monde réel.
Par exemple, le format des dates (section 5.1.1), utilisées dans
des attributs comme Expires
, permet à peu près
toutes les variations possibles. Écrire un
analyseur complet n'est pas trivial, d'autant
plus que le RFC ne décrit pas une grammaire
indépendante du contexte mais un algorithme à état. (Si un pro de
l'analyse syntaxique peut fournir une grammaire plus déclarative de
cette syntaxe, je serais intéressé...)
De même, l'analyse du Set-Cookie
en section 5
est plus complexe car le client doit accepter des
Set-Cookie
plus variés, par exemple avec des
espaces en plus.
La section 5.3, qui n'a pas d'équivalent dans la section 4, décrit la façon dont les clients, les navigateurs Web, doivent stocker les gâteaux et les renvoyer. Elle rappelle la liste des attributs à stocker avec chaque gâteau (par exemple, s'il doit être renvoyé uniquement sur une connexion sécurisée ou bien si ce n'est pas nécessaire). Elle est très longue et le pauvre auteur de navigateur Web a donc bien du travail.
En parlant du programmeur, justement, la section 6 décrit les questions que pose l'implémentation de cette spécification. Parmi elles, la question de la taille du magasin où sont stockés les gâteaux. Le client Web n'est nullement obligé de fournir un magasin de taille infinie. Néanmoins, le RFC demande que le client accepte au moins quatre kilo-octets par gâteau, cinquante gâteaux par domaine et trois mille gâteaux en tout (ce qui permet déjà une belle indigestion). Conséquence de cette limite : un serveur doit s'attendre à ce qu'un client ne renvoie pas tous les gâteaux prévus. Certains ont pu être abandonnés par manque de place.
On l'a vu, la section 5, qui normalise le comportement du client,
est très complexe. La section 4 l'est moins mais l'expérience a
largement prouvé que les serveurs émettant des gâteaux avaient du mal
à se tenir à un profil strict, et produisaient trop de variations. Une
des raisons identifiées par la section 6.2 est le manque
d'API. La plupart du temps, les applications
font directement un printf "Set-Cookie: foobar=1234567;
Expires=Wed, 06 Jun 2012 10:18:14 GMT\n"
(ou équivalent) au
lieu d'appeler une API plus abstraite dont l'implémentation se
chargera de sérialiser proprement les objets complexes comme la
date. Avec tant de liberté laissée au programmeur, il n'est pas
étonnant qu'il y ait des variations non-standard.
Si le RFC ne propose pas de solution, il insiste sur l'importance
pour les programmeurs d'utiliser des bibliothèques bien déboguées et
de ne pas se croire capable d'écrire un
Set-Cookie
correct après cinq minutes de lecture
de la norme. Un exemple d'une telle bibliothèque est, en
Python, http.cookies. D'encore
plus haut niveau, dans le même
langage, urllib2 gère les gâteaux toute seule.
Comme le respect de la vie privée est le principal problème des gâteaux, il est normal que cette question ait une section entière, la 7, qui détaille ce point. C'est inhabituel à l'IETF, où la vie privée ne génère en général que peu d'intérêt. Peu de RFC ont une section Privacy considerations. Que dit-elle ? Que les gâteaux sont effectivement une technologie sensible (bien qu'il existe d'autres moyens de suivre un utilisateur mais moins pratiques donc moins dangereux) et que les plus problématiques sont les gâteaux envoyés à un tiers (c'est-à-dire à une autre organisation que celle qui gère visiblement la page, par le biais d'une discrète image insérée, par exemple). Par exemple, un service de statistiques utilisé sur de nombreux sites Web, ou bien un service de placement de publicités, peuvent permettre à leur gérant de croiser les visites faites sur plusieurs sites différents et ceci sans que l'utilisateur n'ait fait de visite explicite aux services de cette indiscrète organisation. Certains navigateurs sont, à juste titre, davantage paranoïaques lorsque le gâteau est envoyé par un tiers, par exemple en les refusant par défaut. D'autres ne traitent pas ces gâteaux différemment des autres. La question étant essentiellement politique, notre RFC 6265 ne définit pas de comportement normalisé, se contentant d'attirer l'attention sur ce point.
Peut-on demander son avis à l'utilisateur ? C'est ce qu'expose la section 7.2, consacrée au contrôle par l'utilisateur. Le RFC recommande que les clients Web fournissent des mécanismes de contrôle des gâteaux permettant de :
about:config network.cookie.lifetimePolicy=1
),
Voici la liste des gâteaux stockés, telle que Firefox permet de
l'examiner et de la modifier :
Et la question que pose le même Firefox lorsqu'on l'a configuré avec about:config network.cookie.lifetimePolicy=1
:
Et le dialogue de configuration dans Firefox 4, montrant l'option qui permet de demander à chaque gâteau si on l'accepte :
À noter qu'il existe bien d'autres moyens de configurer la politique
des gâteaux, si le navigateur ne fait pas ce qu'on veut, par exemple
des extensions comme Cookie
Monster, WebDeveloper (qui permet de voir tous les gâteaux de la page en cours, avec pour chacun « Éditer » ou « Supprimer ») ou Ghostery
pour Firefox. Le navigateur Dillo, quant à lui,
à un mécanisme très
détaillé.
Même en dehors du cas de la vie privée, les gâteaux posent un certain nombre de problèmes de sécurité (section 8). L'utilisation de HTTPS, quoique évidemment souhaitable, ne protège pas complètement contre ces vulnérabilités.
Premier problème, l'autorité diffuse (ambient authority, section 8.2). Le gâteau utilisé pour l'authentification sépare la désignation (l'URL) de l'autorisation. Le client risque donc d'envoyer le gâteau pour une ressource Web qui était contrôlée par un attaquant, une technique connue sous le nom de CSRF (il existe plusieurs moyens de convaincre un navigateur de se prêter à cette attaque, une étude bien plus détaillée des CSRF, qui a parmi ses auteurs l'auteur de ce RFC, est « Robust Defenses for Cross-Site Request Forgery »). Une solution à ce problème serait de traiter les URL comme une capacité, donnant accès à une ressource. L'URL serait alors le secret à transmettre pour avoir accès (le RFC ne note malheureusement pas que les URL ne restent pas secrets longtemps, par exemple certains navigateurs ou extensions des navigateurs - la Google Toolbar - transmettent les URL consultés à leur maître).
Autre problème des gâteaux, leur envoi en texte clair (section
8.3). Si on n'utilise pas HTTPS, Set-Cookie
et
Cookie
sont transmis en clair avec leur contenu,
permettant à un espion de les copier et de les utiliser (c'est cette
vulnérabilité qu'exploite un programme comme
FireSheep). En outre, un attaquant actif peut
modifier ces en-têtes. La solution recommandée est évidemment de
chiffrer toute la session, et de mettre l'attribut
Secure
aux gâteaux envoyés, pour éviter qu'ils ne
soient plus tard transmis sur une session non chiffrée. Beaucoup de
gros services très populaires ne sont pas accessibles en HTTPS ou bien
ne le mettent pas en œuvre par défaut (souvent pour des raisons
de performance, facteur crucial pour ces services qui reçoivent
beaucoup de visites). Dans le cas de Gmail,
c'est l'option « Général : Connexion du navigateur ; Toujours utiliser
le protocole https ». Dans celui de Twitter,
c'est « Toujours utiliser le HTTPS ». L'EFF
mène une campagne pour systématiser
cette protection. À noter qu'on peut limiter (mais pas
supprimer) la réutilisation du gâteau en liant celui-ci à
l'adresse IP du client ; si le gâteau vient
d'une autre adresse, il sera refusé. Cette méthode n'est pas si
efficace qu'elle en a l'air en raison de la fréquence du partage
d'adresses IP (via le NAT). Ainsi, dans un café
avec WiFi, un attaquant qui utilise FireSheep n'aura pas de problèmes
malgré cette protection car tous les clients sont probablement
derrière la même adresse IPv4 (c'est sans doute
pour cela que le RFC ne propose pas cette solution).
C'est encore pire si le gâteau contient directement de
l'information utile. Par exemple, si le gâteau est
lang=en-AU
, l'espion qui écoute peut trouver la
langue et le pays de l'utilisateur. En général, cette information est
plutôt stockée sur le serveur et le gâteau ne contient qu'un
identificateur de session, inutile si on n'a pas accès au
serveur. Mais cette technique présente d'autres risques (section 8.4),
par exemple celui du « choix de session » où l'attaquant se connecte
au serveur, puis convainct la victime (par exemple par le biais d'un
lien) d'utiliser l'identificateur de session et d'interagir avec le
serveur (par exemple en y déposant des informations
confidentielles). L'attaquant, qui a conservé l'identificateur de
session, pourra alors continuer la session et récupérer ces
informations. (Voir un
exemple et un article
complet.)
Autre problème de sécurité des gâteaux, la confidentialité (section
8.5) : comme les gâteaux ne sont pas spécifiques d'un
port ou d'un plan (http, gopher, ftp, etc), un
client risque toujours de transmettre les gâteaux d'un serveur à un
autre. L'intégrité n'est pas non plus garantie (section 8.6). Si
bar.example.com
a mis un gâteau de domain de
example.com
, un autre serveur,
foo.example.com
peut parfaitement le
remplacer. Path
n'est pas une protection : il
n'empêche pas le remplacement.
Enfin, les gâteaux dépendent du DNS et en héritent les vulnérabilités (section 8.7).
Une très bonne synthèse des failles de sécurité des gâteaux figure dans l'excellent article « HTTP cookies, or how not to design protocols ».
Voilà, c'est tout, il reste à enregistrer les deux en-têtes dans le registre des en-têtes, ce que fait la section 9.
Contrairement aux autres RFC qui remplacent un autre RFC, ce RFC 6265 ne contient pas de description des changements par rapport aux précédents documents. Les clarifications et précisions sont sans doute trop nombreuses pour former une liste intéressante.
Merci à Jean-Baptiste Favre, bohwaz et Ollivier Robert pour leurs remarques et suggestions.
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)