Cookies



Référence : MDN

Un cookie HTTP (également appelé cookie web ou cookie de navigateur) est une donnée de petite taille envoyée par le serveur au navigateur web de l'utilisatrice ou de l'utilisateur. Le navigateur peut alors enregistrer le cookie et le renvoyer au serveur lors des requêtes ultérieures.

Généralement, un cookie HTTP sert à indiquer que plusieurs requêtes proviennent du même navigateur où une personne est connectée. Il permet de mémoriser des informations d'état alors que le protocole HTTP est sans état.

Les utilisations principales des cookies HTTP sont :

  • la gestion de session (authentification, panier d'achat, etc.)
  • la personnalisation (préférences de l'utilisateur, thèmes, etc.)
  • le tracking utilisateur (analytics, publicité ciblée, etc.)

Les cookies étant renvoyés à chaque requête, il est important de ne pas les surcharger d'informations inutiles. D'autres mécanismes de stockage côté client existent, comme le Web Storage (localStorage et sessionStorage) et IndexedDB, qui permettent de stocker des données plus volumineuses et plus structurées, qui ne sont pas transmises au serveur à chaque requête.

Côté serveur, un cookie est créé en ajoutant un en-tête Set-Cookie à la réponse HTTP. Par exemple, en utilisant le framework Bottle, et la méthode set_cookie de l'objet response (référence : response.set_cookie) :

from bottle import response, #...
@route('/set_cookie')
def set_cookie():
    # création d'un cookie nommé "hello" avec la valeur "world"
    response.set_cookie("hello", "world")
    return "Cookie créé !"

La réponse HTTP ressemblera à ceci :

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Set-Cookie: hello=world

Ensuite, le cookie sera envoyé par le navigateur à chaque requête vers le serveur, jusqu'à ce qu'il expire ou soit supprimé. Par exemple :

GET /set_cookie HTTP/2.0
Host: localhost:38083
Cookie: hello=world

Durée de vie

Un cookie peut avoir une durée de vie définie par l'attribut Max-Age ou un date d'expiration définie par l'attribut Expires. Si aucun de ces attributs n'est défini, le cookie sera supprimé lorsque la session du navigateur sera fermée (cookie de session).

# cookie valable 1 heure
response.set_cookie("hello", "world", max_age=3600)
# cookie valable jusqu'au 1er janvier 2026
response.set_cookie("hello", "world", expires="2026-01-01T00:00:00Z")
# cookie de session (supprimé à la fermeture du navigateur)
response.set_cookie("hello", "world", expires=None, max_age=None) 

Chemin et domaine

Un cookie peut être limité à un chemin ou un domaine spécifique. Par défaut, le cookie est valable pour le chemin de la requête et le domaine du serveur. On peut modifier ces valeurs avec les attributs Path et Domain.

# cookie valable pour le chemin /app et le domaine example.com
response.set_cookie("hello", "world", path="/app", domain="example.com")
# cookie valable pour tous les chemins et tous les sous-domaines de example.com
response.set_cookie("hello", "world", path="/", domain=".example.com")

On ne peut pas définir un cookie pour un sous-domaine d'un domaine différent. Par exemple, on ne peut pas définir un cookie pour app.example.com depuis example.org. En revanche, on peut définir un cookie pour example.com depuis app.example.com ou www.example.com.

Par défaut, le cookie est valable pour le chemin de la requête et le domaine du serveur qui l'a créé.

Secure et HttpOnly

Un cookie peut être marqué comme Secure pour indiquer qu'il ne doit être transmis que sur des connexions HTTPS. Cela permet de protéger le cookie contre une interception par un tiers lors de la transmission sur le réseau (ex: réseau Wi-Fi public).

# cookie valable uniquement sur HTTPS
response.set_cookie("hello", "world", secure=True)

Un cookie peut également être marqué comme HttpOnly pour indiquer qu'il ne doit pas être accessible par JavaScript. Cela permet de protéger le cookie contre les attaques de type cross-site scripting (XSS), où un script malveillant pourrait essayer de lire le cookie et d'en voler les informations (ex: session, identifiant de l'utilisateur, etc.).

# cookie non accessible par JavaScript
response.set_cookie("hello", "world", httponly=True)  

SameSite

Un cookie peut être marqué comme SameSite pour indiquer qu'il ne doit pas être transmis avec des requêtes cross-site (requètes faites depuis un autre domaine que celui qui a créé le cookie). Cela permet de protéger le cookie contre les attaques de type cross-site request forgery (CSRF), où un site malveillant pourrait essayer de faire une requête vers un autre site en utilisant les cookies de l'utilisateur

Valeurs possibles :

  • Strict : le cookie n'est pas envoyé avec les requêtes cross-site.
  • Lax : similaire à Strict, mais le navigateur envoie le cookie lorsque la personne navigue vers le site (ex: en cliquant sur un lien).
  • None : le cookie est envoyé avec toutes les requêtes, y compris les requêtes cross-site. Dans ce cas, le cookie doit être marqué comme Secure.

Par défaut, la valeur de SameSite est Lax.

# cookie non envoyé avec les requêtes cross-site
response.set_cookie("hello", "world", samesite="strict")  
# cookie envoyé avec les requêtes cross-site de navigation
response.set_cookie("hello", "world", samesite="lax")
# cookie envoyé avec toutes les requêtes cross-site
response.set_cookie("hello", "world", samesite="none")  

Accès côté serveur

Pour accéder à un cookie côté serveur, on peut utiliser l'objet request du framework Bottle. Par exemple, pour accéder au cookie hello créé précédemment :

from bottle import request
hello = request.get_cookie("hello")  # accès au cookie "hello"
print(hello)  # affiche "world"

On peut également accéder à tous les cookies en utilisant la méthode request.cookies, qui renvoie un dictionnaire contenant tous les cookies du domaine courant.

from bottle import request
cookies = request.cookies  # accès à tous les cookies
print(cookies)  # affiche tous les cookies sous forme de dictionnaire

Accès en JavaScript

Pour accéder à un cookie en JavaScript, on peut utiliser la propriété document.cookie. Cette propriété renvoie une chaîne de caractères contenant tous les cookies du domaine courant, sous la forme nom=valeur; nom=valeur; ....

console.log(document.cookie); // affiche tous les cookies du domaine courant

Les cookies avec les attributs HttpOnly ne sont pas accessibles en JavaScript. Cela permet de protéger les cookies sensibles (ex: session, identifiant de l'utilisateur, etc.) contre les attaques de type cross-site scripting (XSS).

Pistage et vie privée

Une page contenant des images ou des scripts provenant d'autres domaines (ex: page sur example.com et image sur images.com) peut recevoir des cookies de ces domaines. C'est ce qu'on appelle des cookies tiers. Ils peuvent permettre (en définissant SameSite à None), de suivre l'utilisateur sur tous les sites qui utilisent des ressources provenant de images.com. Ceci est utilisé par les régies publicitaires pour enregistrer les visites de l'utilisateur sur différents sites, et pour lui soumettre des publicités ciblées. Ces cookies peuvent être bloqués par certains navigateurs.

En pratique

Compteur

Écrire une vue /compteur qui affiche le nombre de fois que la page a été visitiée par le navigateur. Le compteur doit être stocké dans un cookie nommé compteur. Le cookie doit être valable uniquement pour le chemin /compteur. Le cookie doit expirer à la fin de la session, ne doit pas être accessible en JavaScript.

Correction

Télécharger le code

from bottle import route, run, template, static_file, get, request, response

@route('/compteur')
def compteur():
    # on récupère le cookie "compteur" (s'il existe)
    compteur = request.get_cookie("compteur")
    if compteur is None:
        # si le cookie n'existe pas, on l'initialise à 1
        compteur = 1
    else:
        # sinon, on l'incrémente de 1
        compteur = int(compteur) + 1
    # on crée ou met à jour le cookie "compteur"
    response.set_cookie("compteur", str(compteur), path="/compteur", 
                        httponly=True, max_age=None, expires=None)
    return f"Vous avez visité cette page {compteur} fois."


Programme

  • NSI 1ère : interaction client/serveur, requêtes HTTP, réponses du serveur : distinguer ce qui est mémorisé dans le client et retransmis au serveur