Exercice HTML : Gestion des cookies en JavaScript  

Thème

Présenter la technique des cookies, et exercer la mise en place et lecture de cookies par JavaScript.

Introduction

Les "cookies" (biscuits en traduction française littérale !) désignent un mécanisme permettant au serveur Web d'enregistrer, d'une manière "raisonnablement sûre", de petites informations 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 état ("stateless"), 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 très utilisés par les serveurs de E-commerce pour stocker le profil de l'acheteur, les références des articles de la commande courante (panier d'achat), etc... Le serveur pourra aussi, lors d'une prochaine visite de l'acheteur, pré-remplir certains champs de formulaire (nom, adresse...) et personnaliser l'affichage en fonction des préférences de l'utilisateur (bandeaux publicitaires...). Les mécanismes de gestion de session (p.ex. en PHP) s'appuient également le mécanisme des cookies pour stocker l'identificateur de session.

Initialement défini par Netscape, le mécanisme des cookies est supporté à partir des versions de navigateur Netscape 1 et Internet Explorer 2 ainsi que par tous les navigateurs modernes... (mais l'utilisateur peut le désactiver au niveau des Options de son navigateur Web). Il 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 :

Remarque importante au programmeur Web : 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 ! Exemple: dans le cas d'un script CGI Perl basé CGI.pm, utiliser cookie(...) et header(...) avant de faire start_html(...) et tout print(...).

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...) :

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.

Le contenu des fichiers de cookies peut être examiné par n'importe quel éditeur de texte. Au cours d'une session, l'instruction javascript:alert(document.cookie); , passée dans la zone d'introduction d'URLs, permet d'afficher les cookies manipulés par le serveur courant (serveur dont le nom apparaît dans l'URL de la page courante).

Quelques caractéristiques importantes relatives aux cookies :

L'utilisateur qui n'apprécierait pas que des données plus ou moins personnelles soient enregistrées à son insu puis relues par des serveurs Web, peut paramétrer les options de son navigateur relatives aux cookies (p.ex. avec Netscape 4 sous Edit>Preferences>Advanced, avec Mozilla sous Edit>Preferences>Privacy & Security>Cookies) : refuser les cookies, être avisé chaque fois qu'un serveur crée un cookie, manager ces cookies... Par ailleurs, la désactivation de JavaScript sur le navigateur empêchera 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.

Les différents champs d'un cookie

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.

   enacit1.epfl.ch  FALSE  /www_test/cookies  FALSE  1023387128  visites     31
   .dell.com        FALSE  /                  FALSE  1025388654  language    fr
   .dell.com        FALSE  /                  FALSE  1024630783  ClientMAIL  Jean-Daniel.Bonjour@epfl.ch
   .dell.com        FALSE  /                  TRUE   1024630792  ClientNR    1265346753
Remarques :

Fonctions JavaScript relatives aux cookies

Le modèle objet utilisé par JavaScript (DOM, Document Object Model) définit les cookies comme des propriétés de documents, c'est-à-dire sous la propriété document.cookie.

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 (partie <HEAD> ... </HEAD>), le bloc ci-dessous. Il s'agit des fonctions JavaScript de manipulation de cookies les plus connues, développées par Bill Dortch.


  <SCRIPT LANGUAGE="JavaScript">
  <!--
    //******************************************************************************
    //  Cookie Functions -- "Night of the Living Cookie" Version (25-Jul-96)
    //  Written by:  Bill Dortch, hIdaho Design <bdortch@hidaho.com>
    
    
    //  "Internal" function to return the decoded value of a cookie
    function getCookieVal (offset) {
      var endstr = document.cookie.indexOf (";", offset);
      if (endstr == -1)
        endstr = document.cookie.length;
      return unescape(document.cookie.substring(offset, endstr));
    }
    
    
    //  Function to return the value of the cookie specified by "name".
    //    name -    String object containing the cookie name.
    //    returns - String object containing the cookie value,
    //              or null if the cookie does not exist.
    //
    function GetCookie (name) {
      var arg = name + "=";
      var alen = arg.length;
      var clen = document.cookie.length;
      var i = 0;
      while (i < clen) {
        var j = i + alen;
        if (document.cookie.substring(i, j) == arg)
          return getCookieVal (j);
    	i = document.cookie.indexOf(" ", i) + 1;
        if (i == 0) break; 
      }
      return null;
    }
    
    
    //  Function to create or update a cookie.
    //    name - String object containing the cookie name.
    //    value - String object containing the cookie value.  May contain
    //         any valid string characters.
    //    [expires] - Date object containing the expiration data of the cookie.  If
    //         omitted or null, expires the cookie at the end of the current session.
    //    [path] - String object indicating the path for which the cookie is valid.
    //         If omitted or null, uses the path of the calling document.
    //    [domain] - String object indicating the domain for which the cookie is
    //         valid. If omitted or null, uses the domain of the calling document.
    //    [secure] - Boolean (true/false) value indicating whether cookie
    //         transmission requires a secure channel (HTTPS).  
    //
    //  The first two parameters are required.  The others, if supplied, must
    //  be passed in the order listed above.  To omit an unused optional field,
    //  use null as a place holder.  For example, to call SetCookie using name,
    //  value and path, you would code:
    //
    //      SetCookie ("myCookieName", "myCookieValue", null, "/");
    //
    //  Note that trailing omitted parameters do not require a placeholder.
    //
    //  To set a secure cookie for path "/myPath", that expires after the
    //  current session, you might code:
    //
    //      SetCookie (myCookieVar, cookieValueVar, null, "/myPath", null, true);
    //
    function SetCookie (name,value,expires,path,domain,secure) {
      document.cookie = name + "=" + escape (value) +
        ((expires) ? "; expires=" + expires.toGMTString() : "") +
        ((path) ? "; path=" + path : "") +
        ((domain) ? "; domain=" + domain : "") +
        ((secure) ? "; secure" : "");
    }
    
    
    //  Function to delete a cookie. (Sets expiration date to start of epoch)
    //    name -   String object containing the cookie name
    //    path -   String object containing the path of the cookie to delete.
    //             This MUST be the same as the path used to create the cookie, or
    //             null/omitted if no path was specified when creating the cookie.
    //    domain - String object containing the domain of the cookie to delete.
    //             This MUST be the same as the domain used to create the cookie, or
    //             null/omitted if no domain was specified when creating the cookie.
    //
    function DeleteCookie (name,path,domain) {
      if (GetCookie(name)) {
        document.cookie = name + "=" +
          ((path) ? "; path=" + path : "") +
          ((domain) ? "; domain=" + domain : "") +
          "; expires=Thu, 01-Jan-70 00:00:01 GMT";
      }
    }
    
    //******************************************************************************
  // -->
  </SCRIPT>
  

Usage de ces fonctions

SetCookie(name,value,expires,path,domain,secure)
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, il faut 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. 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.

Autres remarques

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">
    <!--
    nom_var = GetCookie("nom");
    if (nom_var == null) {
      nom_var = prompt("Quel est votre nom ?","");
      duree = 10;  // Durée de vie du cookie en jours
      date_expire = new Date();
      date_expire.setTime(date_expire.getTime() + (duree*24*60*60*1000));
      SetCookie("nom",nom_var,date_expire);
    }
    document.writeln("<H2 ALIGN=center>Hello " + nom_var + "</H2>");
    // -->
  </SCRIPT>

Références

Exercice

Développez une page HTML implémentant les fonctionnalités illustrées lar la figure ci-dessous, à savoir : Remarque : on pourrait aussi demander interactivement à l'utilisateur les informations à stocker sous forme de cookies par JavaScript plutôt que via un formulaire HTML, par exemple : nom = prompt("Quel est votre nom ?","");


Une fois la page réalisée, testez son fonctionnement, en particulier :

Vous pouvez accéder par ce lien au corrigé de cet exercice (mais, dans le navigateur, un View > Page Source ne montre pas tout le code JavaScript présent dans la page !).

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.


Exercices HTML © J.-D. Bonjour / ENAC-IT1, EPFL / Révision 25.2.2003