Les "cookies" désignent un mécanisme permettant au serveur Web d'enregistrer, de petites informations (<4ko) localement du coté client (navigateur Web) lors d'une transaction, puis d'accéder à ces informations (les lire, mettre à jour, effacer) lors d'une transaction ultérieure. HTTP étant un protocole sans mémoire, le but des cookies est de faire maintenir par le client des données persistantes qui seront retransmises au serveur lors d'autres transactions.
Dans la pratique, les cookies sont utilisés par les serveurs pour gérer les sessions, stocker un profil, un panier d'achats, etc... ou personnaliser l'affichage en fonction des préférences de l'utilisateur (bandeaux publicitaires...). Les mécanismes de gestion de session PHP s'appuient également sur le mécanisme des cookies.
Le mécanisme des cookies est supporté par tous les navigateurs modernes et est maintenant défini par la RFC 2109 "HTTP State Management Mechanism".
Véhiculés par les en-têtes HTTP Set-Cookie (réponse serveur->client) et Cookie (requête client->serveur), les cookies peuvent être manipulés de différentes façons :
en JavaScript par les fonctions présentées ci-dessous
en programmation CGI :
par envoi de l'en-tête HTTP Set-Cookie et via la variable CGI HTTP_COOKIE
en PHP par la fonction setcookie(...) et la variable-tableau "superglobale" $_COOKIE["nom_cookie"]
Les cookies étant véhiculés sous forme d'en-têtes HTTP, la mise en place de cookies doit s'effectuer durant la phase d'envoi au navigateur des lignes d'en-têtes, donc avant la génération du corps de la réponse !
Au cours d'une session Web, le navigateur manipule les cookies en mémoire vive. Lorsque l'on quitte le navigateur Web, celui-ci les enregistre automatiquement sur disque, dans des fichiers et à des emplacements qui diffèrent selon les navigateurs (Netscape/Mozilla, Internet Explorer...) et les plateformes (Windows, Macintosh, Unix...) :
Netscape/Mozilla mémorise tous les cookies dans un seul fichier de nom cookies.txt (Windows), MagicCookie (Macintosh) ou cookies (Unix) ; sous Windows, celui-ci se trouve dans le dossier de profil Netscape/Mozilla de l'utilisateur (sous Win 2K/XP : C:\Documents and Settings\username\Application Data\Mozilla\Profiles\nom_profil\xxxxxxxx.slt)
Internet Explorer mémorise les cookies dans plusieurs fichiers séparés, nommés username@nom_serveur.txt et localisés dans le dossier COOKIES du profil Windows de l'utilisateur (C:\Documents and Settings\username sous Win2K/XP)
Inversement, lorsque l'on démarre le navigateur, le fichier de cookies est lu par le navigateur. L'activité proprement dite du navigateur relative aux cookies est donc réalisée en mémoire vive.
L'instruction javascript:alert(document.cookie); , permet d'afficher les cookies manipulés par le serveur courant.
Quelques caractéristiques importantes relatives aux cookies :
la taille d'un cookie (son nom + sa valeur) ne peut dépasser 4'000 octets (la valeur étant automatiquement tronquée en cas de dépassement cette taille)
le navigateur accepte au maximum 20 cookies par domaine
le navigateur ne mémorise au total que 300 cookies (les cookies les plus anciens étant automatiquement détruits quand de nouveaux sont créés par un serveur)
un serveur ne peut par défaut manipuler (lire, mettre à jour, effacer) que les cookies qu'il a lui-même créés (...sous réserve de trous de sécurité du logiciel navigateur !)
La désactivation de JavaScript sur le navigateur empêche toute manipulation de cookies par JavaScript (mais pas par CGI, PHP...). Il faut cependant être attentif que, sur les sites de E-commerce notamment, le refus des cookies par le navigateur peut empêcher toute transaction d'achat ; les systèmes de gestion de session s'appuyant sur les cookies (identificateur de session) ne fonctionneront pas non plus.
Un "enregistrement" de cookie se compose des champs suivants :
domaine
Nom du serveur et de domaine Internet (serveur.domaine.tld) pour lesquels l'accès au cookie est validé.
Ce champ peut contenir un nom incomplet de type .domaine.tld (avec au minimum 2 points),
ce qui validera l'accès au cookie à tous les serveurs du domaine spécifié
path
Chemin de l'URL (partie qui suit http://serveur.domaine.tld) définissant l'arborescence des
ressources, sur le serveur, autorisées à accéder au cookie.
Si l'on définit simplement /, toutes les "pages" du serveur.domaine.tld pourront alors
accéder à ce cookie
Si l'on définit un path sans spécifier de "page" Web, toutes les "pages" se trouvant dans le répertoire
correspondant et ses sous-répertoires pourront alors accéder au cookie
Si l'on précise le path en le terminant par un nom de "page" Web, alors seule cette "page" pourra accéder au cookie
Par "page" il faut entendre bien évidemment tout type de ressource Web (page HTML, page PHP, script CGI...).
sécurité
Valeur logique (TRUE ou FALSE) indiquant s'il est nécessaire, respectivement pas nécessaire, de réaliser une transaction sécurisée (SSL) pour accéder au cookie.
expiration
Date et heure d'expiration du cookie. Au niveau de la transaction HTTP, cette date/heure est véhiculée en temps
absolu au format GMT (p.ex. Sat 23-Mar-02 12:30:55 GMT), mais elle est stockée différemment dans les fichiers
de cookies.
Au-delà de cette date, le cookie sera automatiquement éliminé du fichier de
cookies par le navigateur
Lorsqu'un cookie est mis en place sans que soit spécifiée une date d'expiration, celui-ci sera
temporaire, c'est-à-dire qu'il disparaîtra automatiquement lorsque l'on quittera le navigateur (c-à-d. ne sera pas
enregistré dans le fichier de cookies).
nom
Nom du cookie.
valeur
Valeur (chaîne de caractère) associée au nom du cookie. Au niveau de la transaction HTTP et dans le fichier
de cookies, les caractères spéciaux (en particulier "point-virgule", "virgule", "espace",
"tab") ne peuvent pas être utilisés tels quels, raison pour laquelle les fonctions JavaScript de
manipulation de cookies les encodent/décodent avec les fonctions escape() et unescape()
(remplacement du caractère par son code ASCII hexadécimal, p.ex. <espace> devient %20).
Si l'on examine le fichier cookies.txt de Netscape/Mozilla, on retrouve en clair les valeurs relatives à ces différents champs (les séparateurs entre les différents champs sont des caractères <TAB>). Les cookies les plus récemment mis en
place se trouvent au haut du fichier.
la date d'expiration, dans le fichier de cookies de Netscape/Mozilla, est exprimée en nombre de secondes écoulées depuis le 1er janvier 1970
la seconde valeur booléene correspond au champ "sécurité" (à quoi correspond donc la 1ère valeur logique ?)
le format des fichiers de cookies Internet Explorer est tout différent, mais on y retrouve ces différents champs à raison d'un champ par ligne (les dates étant formatées différemment)
Dans toute page HTML devant manipuler (créer, lire, modifier ou effacer) des cookies par JavaScript, on insèrera, dans l'en-tête de la page le fichier cookies.js. Il s'agit des fonctions JavaScript de manipulation de cookies les plus connues.
Fonction de mise en place ou de modification d'un cookie.
Les paramètres name (nom du cookie) et value (valeur du cookie) sont obligatoires, les
autres étant facultatifs. Rappelons que la paire name/value ne peut pas excéder 4'000 octets.
Tous les paramètres doivent être passés dans l'ordre spécifié. Si un paramètre est omis, on peut
passer l'argument null à sa place .
Si le paramètre expires est omis, le cookie est temporaire (expirera lorsque l'on
quittera le navigateur)
On spécifie généralement la date d'expiration à partir de la date courante en lui ajoutant la durée
de vie désirée du cookie (définie en millisecondes) par les instructions JavaScript suivantes :
duree_cookie = 100; // durée de vie du cookie en jours
expiration = new Date(); // date et heure courante en format texte
expiration.setTime(expiration.getTime() + (duree_cookie * 24*60*60*1000));
// => on peut utiliser la variable "expiration"
Si le paramètre path est omis, le path enregistré avec le cookie sera celui du répertoire
de la page ayant mis en place le cookie, et donc toutes les pages se trouvant dans ce répertoire ainsi que
dans les répertoires subordonnés auront ultérieurement accès à ce cookie. Pour limiter l'accès au cookie à votre "page"
uniquement, il faudrait donc spécifier un path complet incluant le nom de la "page" (on l'obtient, en JavaScript,
avec path = location.pathname) ! A l'opposé, si toutes les "pages" d'un site doivent pouvoir accéder au
cookie, il faudrait l'enregistrer avec le path/
Si le paramètre domain est omis, le domain enregistré avec le cookie sera le nom complet
serveur.domaine.tld du serveur (équivalent, en JavaScript. à location.hostname) ayant
mis en place le cookie ; si la page est locale, c-à-d. avec URL file://..., le domain enregistré sera
vide. Lorsque l'on spécifie un domain, il est possible d'omettre le nom de serveur, c'est-à-dire spécifier
uniquement .domaine.tld, ce qui permettra à tous les serveurs du domaine en question d'accéder au
cookie (pour autant que l'on aie par ailleurs spécifié le path/).
Quant au paramètre secure, il permet de spécifier avec la valeur true que le cookie ne
sera ensuite accessible que via une transaction sécurisée SSL (protocole HTTPS). Si l'on omet ce paramètre lors de
l'enregistrement d'un cookie, il sera enregistré en mode false et donc accessible via des sessions HTTP
standards (non sécurisées)
GetCookie(name)
Fonction de récupération de la valeur du cookie dont on spécifie le nom.
La valeur retournée sera "null" si aucun cookie du nom spécifié n'a pû être récupéré, soit
parce que le cookie n'existe pas, ou parce que les permissions sont insuffisantes (requête provenant d'un autre
domain où n'ayant pas le path correct).
DeleteCookie(name,path,domain)
Fonction d'effacement d'un cookie. Le "path" doit être le même. Il sera supprimé du fichier de cookies à la sortie du navigateur. (Si l'on examine
le code de cette fonction, on voit que le cookie est en fait supprimé en le ré-enregistrant avec une date d'expiration appartenant au passé).
Si le cookie a été créé sans spécifier de path et de domain, ces paramètres
peuvent être omis ; sinon ils doivent être spécifiés et correspondre à ceux qui avaient été définis lors de la
création du cookie. Pour que l'opération réussisse, il faut que la requête provienne du bon domain et
ayant le path correct.
Si vous testez le mécanisme des cookies avec IE3-, sachez qu'il n'est pas capable de créer de cookies et d'y accéder à partir de fichiers HTML locaux (non servis par un serveur Web).
Si vous développez des pages Web utilisant le mécanisme des cookies, ne stockez pas d'informations confidentielles (mot de passe, No de carte de crédit...) sous forme de cookies survivant à la session, car quiconque ayant physiquement accès à la machine pourrait donc afficher le contenu du fichier de cookies et y trouver ces informations. Les informations sensibles stockées sous forme de cookies devraient être encryptées, coté serveur, avant leur mise en place sur le navigateur.
Dans le cas d'une application de E-commerce, le traitement du "panier électronique" peut en général être traité par des cookies temporaires.
L'exemple "le plus simple possible":
Nous donnons, ci-dessous, le code JavaScript à placer dans le <BODY> d'une page HTML (qui doit par ailleurs contenir, dans sa partie <HEAD>, les fonctions JavaScript de cookies présentées ci-dessus). Celui-ci demande, dans une fenêtre "prompt" JavaScript, le nom de l'utilisateur qu'il enregistre ensuite dans un cookie.
<SCRIPT LANGUAGE="JavaScript">
<!--
valeur = GetCookie("nom");
if (valeur == null) {
valeur = prompt("Quel est votre nom ?","");
SetCookie("nom",valeur);
}
document.writeln("<H3 ALIGN=center>Bonjour " + valeur + "</H3>");
// -->
</SCRIPT>
Sécurité
Attaque XSS: Cross Site Scripting
L'idée: l'attaquant insére un script(1) sur la page attaquée, celui-ci appelant un site pirate (2) et lui transmettant le contenu du cookie. A partir de ces données, l'attaquant peut se loguer sur le site attaqué sous une fausse identité (3).
(1) par exemple en insérant du HTML via <TEXTAREA> sur la page attaquée.
(2) par exemple via AJAX ou via une image 1x1 invisible contenant les données dans la QueryString
(3) en copiant les cookies dans son navigateur
Parade: "échapper" le HTML, limiter les balises ou utilisation d'un language de markup
Attaque CSRF
Si je surfe sur un site malveillant "malveillant.com" celui-ci peut de façon secrète me faire poster à mon insu un formulaire(1) sur une URL FaceBook (J'aime), Twitter, ou autre. Comme mon navigateur a la bonne IP et les bons cookies, il sera reconnu automatiquement. Si pas de vérification du "referrer".
Développez une page HTML implémentant les fonctionnalités suivantes :
les éléments suivants seront mémorisés sur des cookies :
nom de l'utilisateur (durée de vie 100 jours)
nombre de visites de la page (durée de vie 100 jours)
date et heure de la dernière visite (durée de vie 100 jours)
couleur de fond de page préférée (cookie temporaire de durée limitée à la session)
à cet effet, cette page présentera donc un formulaire permettant à l'utilisateur de saisir son nom et de choisir la couleur de fond préférée
lors de la première visite, la page souhaitera la "Bienvenue !" à l'utilisateur
lors des visites ultérieures, elle le saluera par un "Bonjour" suivi de son nom (récupéré du cookie), et lui indiquera le nombre de fois qu'il a visité la page ainsi que la date/heure de sa dernière visite ; elle actualisera également automatiquement la durée des cookies
pour faciliter le changement éventuel du nom, le formulaire affichera automatiquement le nom courant dans le champ de saisie du nom
à des fins didactiques, on réalisera un lien permettant d'afficher, par la fonction javascript:alert(document.cookie);, les cookies manipulés par la page (ou accessibles par cette page)
Remarque : on peut demander interactivement à l'utilisateur les informations à stocker sous forme de cookies par JavaScript ou via un formulaire HTML, par exemple : nom = prompt("Quel est votre nom ?","");
Une fois la page réalisée, testez son fonctionnement, en particulier :
examinez les cookies mis en place dans le fichier de cookies
voyez comment le navigateur réagit en fonction des différents réglages relatifs aux cookies
REMARQUE IMPORTANTE : Dans cet exemple, les cookies n'ont été "révélés" par le navigateur qu'à la page Web (celle-ci fonctionnant même "en local", sans l'intermédiaire d'un serveur Web). Pour récupérer ces cookies sur un serveur Web, il suffirait de les lire sur la variables CGI HTTP_COOKIE ou sur la variable PHP $_COOKIE["nom_cookie"], ou les faire "remonter" dans le cadre d'un formulaire, c'est-à-dire via une requête POST ou GET.