# Documentation Django 5.2.8

# Utilisation des sessions

Django prend complètement en charge les sessions anonymes. L’infrastructure des sessions permet de stocker et de récupérer des données arbitraires sur la base « un site-un visiteur ». Les données sont stockées côté serveur ; l’envoi et la réception des cookies sont transparents. Les cookies contiennent un identifiant de session, et non pas les données elles-mêmes (sauf si vous utilisez le moteur basé sur les cookies).

## Activation des sessions

Les sessions sont implémentées par l’intermédiaire d’un composant middleware.

Pour activer la fonctionnalité des sessions, faites ce qui suit :

- Contrôlez que le réglage `MIDDLEWARE` contient bien `'django.contrib.sessions.middleware.SessionMiddleware'`. Le fichier `settings.py` créé par défaut par `django-admin startproject` active `SessionMiddleware`.

Si vous ne souhaitez pas utiliser les sessions, vous pouvez très bien enlever la ligne `SessionMiddleware` de `MIDDLEWARE` et la ligne `'django.contrib.sessions'` de `INSTALLED_APPS`. Cela économisera un peu de travail à Django.

## Configuration du moteur de sessions

Par défaut, Django stocke les sessions dans la base de données (avec le modèle `django.contrib.sessions.models.Session`). Bien que cela puisse être pratique, il est plus rapide dans certaines configurations de stocker les données de sessions ailleurs ; il est donc possible de configurer Django pour stocker les données de sessions sur le système de fichiers ou dans un cache.

### Utilisation des sessions en base de données

Si vous souhaitez placer le contenu des sessions en base de données, vous devez ajouter `'django.contrib.sessions'` dans le réglage `INSTALLED_APPS`.

Après avoir configuré votre installation, lancez `manage.py migrate` pour installer l’unique table de base de données qui stocke les données des sessions.

### Utilisation des sessions en cache

Pour de meilleures performances, il est recommandé d’utiliser le moteur de sessions basé sur le cache.

Pour stocker les données de sessions en utilisant le système de cache de Django, il s’agit d’abord de s’assurer que le cache est configuré ; voir la documentation du cache pour plus de détails.

Avertissement

Les données de sessions ne devraient être stockées en cache que quand le moteur de cache Memcached ou Redis est utilisé. Le moteur de cache en mémoire locale ne retient pas assez longtemps les données pour qu’il représente un bon choix, et il est plus rapide d’utiliser directement le moteur de sessions basé sur des fichiers ou sur la base de données que de faire transiter le tout par les moteurs de cache basés sur des fichiers ou sur la base de données. De plus, le moteur de cache en mémoire locale ne gère PAS correctement le mode multi-processus et n’est donc probablement pas un bon choix dans un environnement de production.

Si plusieurs caches sont définis dans `CACHES`, Django utilise le cache par défaut. Pour utiliser un autre cache, définissez `SESSION_CACHE_ALIAS` avec le nom de ce cache.

Une fois que votre cache est configuré, vous devez choisir entre un cache basé sur la base de données et un cache non persistant.

Le moteur basé sur la base de données avec cache (cached_db`) utilise un cache à écriture immédiate – les écritures de sessions sont appliquées à la fois à la base de données et au cache, dans cet ordre. Si l’écriture dans le cache échoue, l’exception est interceptée et journalisée via le journaliseur des sessions, pour éviter de faire échouer l’opération d’écriture qui s’est fait correctement en base de données.

Les lectures de sessions utilisent d’abord le cache, puis la base de données si les données recherchées ont été évincées du cache. Pour utiliser ce moteur, définissez `SESSION_ENGINE` à `"django.contrib.sessions.backends.cached_db"` et suivez les instructions de configuration dans utilisation des sessions stockées en base de données.

Le moteur de sessions en cache (`cache`) stocke les données des sessions uniquement dans le cache. C’est plus rapide car on évite la persistance en base de données, mais il faut prendre en compte ce qui arrive quand les données du cache sont purgées. La purge peut arriver si le cache est plein ou si le serveur de cache est redémarré, et cela va provoquer la perte des données de sessions, y compris pour les utilisateurs se déconnectant. Pour utiliser ce moteur, définissez `SESSION_ENGINE` à `"django.contrib.sessions.backends.cache"`.

Le moteur en cache peut être rendu persistant en utilisant un cache persistant, tel que Redis avec une configuration appropriée. Mais tant que le cache n’est pas complètement configuré avec la persistance nécessaire, préférez le moteur basé sur la base de données avec cache. Cela évite des cas limites provoqués par un stockage de données non fiable en production.

### Utilisation des sessions basées sur des fichiers

Pour utiliser les sessions basées sur des fichiers, définissez le réglage `SESSION_ENGINE` à `"django.contrib.sessions.backends.file"`.

Il peut aussi être souhaitable de définir le réglage `SESSION_FILE_PATH` (dont la valeur par défaut est le résultat de `tempfile.gettempdir()`, généralement `/tmp`) pour contrôler l’emplacement où Django stocke les fichiers de sessions. Vérifiez que le serveur web dispose des permissions en lecture et écriture à cet endroit.

### Utilisation des sessions basées sur les cookies

Pour utiliser les sessions basées sur les cookies, définissez le réglage `SESSION_ENGINE` à `"django.contrib.sessions.backends.signed_cookies"`. Les données de sessions seront stockées en utilisant les outils de Django pour la signature cryptographique et le réglage `SECRET_KEY`.

Note

Il est recommandé de laisser le réglage `SESSION_COOKIE_HTTPONLY` à la valeur `True` pour empêcher l’accès à des données stockées à partir du JavaScript.

Avertissement

** les données de sessions sont signées mais non chiffrées**

Lors de l’utilisation du moteur basé sur les cookies, les données de sessions peuvent être lues par le client.

Un code MAC (Message Authentication Code) est utilisé pour protéger les données contre des modifications par le client, afin que les données de sessions soient caduques quand elles ont été modifiées. La même protection intervient si le client qui stocke le cookie (par ex. le navigateur de l’utilisateur) ne peut pas stocker tout le cookie de session et tronque des données. Même si Django compresse les données, il est toujours possible de dépasser la limite habituelle de 4096 octets par cookie.

Aucune garantie de fraîcheur

Il faut également relever que bien que le code MAC puisse garantir l’authenticité des données (qu’elles ont bien été générées par votre site) ainsi que l’intégrité de ces données (qu’elles sont complètes et correctes), il ne peut pas garantir leur fraîcheur, c’est-à-dire que vous recevez la dernière version que vous avez envoyée au client. Cela signifie que pour certaines utilisations des données de session, le moteur par cookie pourrait vous rendre vulnérable à des attaques par rejeu. Au contraire d’autres moteurs de session qui conservent une version de chaque session du côté serveur et qui les infirment lorsqu’un utilisateur se déconnecte, les sessions basées sur les cookies ne sont pas infirmées lorsqu’un utilisateur se déconnecte. Ainsi, si un attaquant vole un cookie d’utilisateur, il peut utiliser celui-ci pour se connecter sous le nom de cet utilisateur même quand ce dernier s’est déconnecté. Les cookies ne sont considérées comme obsolètes qu’au moment où leur âge dépasse `SESSION_COOKIE_AGE`.

Performance

Finalement, la taille d’un cookie peut influencer la rapidité de votre site.

## Utilisation des sessions dans les vues

Lorsque `SessionMiddleware` est actif, chaque objet `HttpRequest`, le premier paramètre de toute fonction vue de Django, possède un attribut `session` qui est un objet de type dictionnaire.

Vous pouvez lire et écrire dans `request.session` à n’importe quel moment dans votre vue. Vous pouvez le modifier plusieurs fois.

class backends.base.SessionBase

  C’est la classe de base pour tous les objets session. Elle possède les méthodes de dictionnaire standard suivantes :

  __getitem__(key)

    Exemple :

  __setitem__(key, value)

    Exemple : `request.session['fav_color'] = 'blue'`

  __delitem__(key)

    Exemple : `del request.session['fav_color']`. Si la clé indiquée ne se trouve pas déjà dans l’objet session, une exception `KeyError` est levée.

  __contains__(key)

    Exemple : `'fav_color' in request.session`

  get(key, default=None)
  aget(key, default=None)

    Version asynchrone: `aget()`

    Exemple : `fav_color = request.session.get('fav_color', 'red')`

  aset(key, value)

    Exemple : `await request.session.aset('fav_color', 'red')`

  update(dict)
  aupdate(dict)

    Version asynchrone: `aupdate()`

    Exemple : `request.session.update({'fav_color': 'red'})`

  pop(key, default=__not_given)
  apop(key, default=__not_given)

    Version asynchrone: `apop()`

    Exemple : `fav_color = request.session.pop('fav_color', 'blue')`

  keys()
  akeys()

    Version asynchrone: `akeys()`

  values()
  avalues()

    Version asynchrone: `avalues()`

  has_key(key)
  ahas_key(key)

    Version asynchrone: `ahas_key()`

  items()
  aitems()

    Version asynchrone: `aitems()`

  setdefault()
  asetdefault()

    Version asynchrone: `asetdefault()`

  clear()

  Elle possède aussi ces méthodes :

  flush()
  aflush()

    Version asynchrone: `aflush()`

    Deletes the current session data from the session and deletes the session cookie. This is used if you want to ensure that the previous session data can’t be accessed again from the user’s browser (for example, the `django.contrib.auth.logout()` function calls it).

  set_test_cookie()
  aset_test_cookie()

    Version asynchrone : `aset_test_cookie()`

    Crée un cookie de test pour déterminer si le navigateur de l’utilisateur prend en charge les cookies. En raison du fonctionnement des cookies, vous ne pourrez pas déterminer cela avant la prochaine requête de l’utilisateur. Consultez Génération de cookies de test ci-dessous pour obtenir plus d’informations.

  test_cookie_worked()
  atest_cookie_worked()

    Version asynchrone : `atest_cookie_worked()`

    Renvoie `True` ou `False`, selon que le navigateur de l’utilisateur a accepté le cookie de test ou non. En raison du fonctionnement des cookies, vous aurez dû appeler `set_test_cookie()` ou `aset_test_cookie()` sur une requête de page précédente. Consultez Génération de cookies de test ci-dessous pour obtenir plus d’informations.

  delete_test_cookie()
  adelete_test_cookie()

    Version asynchrone : `adelete_test_cookie()`

    Supprime le cookie de test. Utilisé pour faire le ménage derrière soi.

  get_session_cookie_age()

    Renvoie la valeur du réglage `SESSION_COOKIE_AGE`. Ceci peut être surchargé dans un moteur de session personnalisé.

  set_expiry(value)
  aset_expiry(value)

    Version asynchrone: `aset_expiry()`

    Définit la durée d’expiration de la session. Il est possible de passer plusieurs valeurs différentes :

    - Si `value` est un nombre entier, la session va expirer après ce nombre de secondes d’inactivité. Par exemple, l’appel de `request.session.set_expiry(300)` va provoquer l’expiration de la session dans 5 minutes.

    - Si `value` est un objet `datetime` (date/heure) or `timedelta` (différence de temps), la session va expirer au moment précis correspondant à la valeur transmise.

    - Si la `value` est `0`, le cookie de session de l’utilisateur va expirer au moment où celui-ci ferme son navigateur web.

    - Si `value` est `None`, la session se fie à nouveau à la politique d’expiration globale des sessions.

    La lecture d’une session n’est pas considérée comme une activité en regard de la notion d’expiration. L’expiration de la session est calculée à partir du moment de la dernière modification de la session.

  get_expiry_age()
  aget_expiry_age()

    Version asynchrone: `aget_expiry_age()`

    Renvoie le nombre de secondes restant jusqu’à l’expiration de la session. Pour les sessions sans expiration particulière (ou celles qui expirent à la fermeture du navigateur), cette valeur sera égale à `SESSION_COOKIE_AGE`.

    Cette fonction accepte deux paramètres nommés facultatifs :

    - `modification`: dernière modification de la session, comme objet `datetime`. Par défaut, c’est l’heure actuelle.

    - `expiry`: information d’expiration de la session, comme objet `datetime`, valeur `int` (en secondes) ou `None`. Par défaut, cette valeur est égale à celle stockée dans la session par `set_expiry()`/`aset_expiry()`, le cas échéant, sinon `None`.

    Note

    Cette méthode est utilisée par les moteurs de session pour déterminer l’âge d’expiration de session en secondes au moment d’enregistrer la session. Elle n’est pas vraiment appropriée pour des utilisations en dehors de ce contexte.

    En particulier, bien qu’il soit possible de déterminer la durée de vie restante d’une session juste au moment où vous avez la valeur `modification` correcte et que `expiry` est défini comme objet `datetime`, dans le cas où vous disposez de la valeur `modification`, il est plus direct de calculer l’expiration à la main

        expires_at = modification + timedelta(seconds=settings.SESSION_COOKIE_AGE)

  get_expiry_date()
  aget_expiry_date()

    Version asynchrone: `aget_expiry_date()`

    Renvoie la date à laquelle la session expire. Pour les sessions sans expiration particulière (ou celles qui expirent à la fermeture du navigateur), cette valeur sera égale à `SESSION_COOKIE_AGE` secondes à partir de maintenant.

    Cette fonction accepte les mêmes paramètres mots-clés que `get_expiry_age()`, et les mêmes notes sur son utilisation s’appliquent.

  get_expire_at_browser_close()
  aget_expire_at_browser_close()

    Version asynchrone: `aget_expire_at_browser_close()`

    Renvoie `True` ou `False` selon que le cookie de session utilisateur expire au moment de la fermeture du navigateur web de l’utilisateur ou non.

  clear_expired()
  aclear_expired()

    Version asynchrone: `aclear_expired()`

    Supprime les sessions expirées du stockage des sessions. Cette méthode de classe est appelée par `clearsessions`.

  cycle_key()
  acycle_key()

    Version asynchrone: `acycle_key()`

    Creates a new session key while retaining the current session data. `django.contrib.auth.login()` calls this method to mitigate against session fixation.

### Sérialisation des sessions

Par défaut, Django sérialise les données de session en JSON. Vous pouvez utiliser le réglage `SESSION_SERIALIZER` pour personnaliser le format de sérialisation de session. Même en considérant les limites décrites dans Écriture de son propre sérialiseur, nous recommandons fortement d’en rester à la sérialisation JSON, d’autant plus si vous utilisez le moteur cookie.

Par exemple, voici un scénario d’attaque si vous utilisez `pickle` pour sérialiser les données de session. Si vous utilisez le moteur de session basé sur des cookies signés et que la clé `SECRET_KEY` (ou une autre clé de `SECRET_KEY_FALLBACKS`) est connue d’un attaquant (il n’y a aucune vulnérabilité connue dans Django qui divulguerait cette information), cet attaquant pourrait insérer une chaîne dans la session, ce qui pourrait conduire, au moment de la désérialisation (« unpickling »), à une exécution de code arbitraire sur le serveur. La technique pour faire cela est simple et aisément disponible sur Internet. Même si le stockage de sessions par cookies signe les données de session pour empêcher leur falsification, une divulgation de la clé `SECRET_KEY` crée immédiatement une vulnérabilité d’exécution de code à distance.

#### Sérialiseurs intégrés

class serializers.JSONSerializer

  Un adaptateur du sérialiseur JSON provenant de `django.core.signing`. Ne peut sérialiser que des types de données simples.

  De plus, comme JSON ne gère que des clés textuelles, il faut savoir que des clés non textuelles dans `request.session` ne fonctionneront pas comme prévu :

      >>> # initial assignment
      >>> request.session[0] = "bar"
      >>> # subsequent requests following serialization & deserialization
      >>> # of session data
      >>> request.session[0]  # KeyError
      >>> request.session["0"]
      'bar'

  De même, les données qui ne peuvent pas être codées en JSON telles que des octets non UTF-8 comme `'\xd9'` (ce qui produit `UnicodeDecodeError`), ne peuvent pas être stockées.

  Consultez la section Écriture de son propre sérialiseur pour plus de détails sur les limites de la sérialisation JSON.

#### Écriture de son propre sérialiseur

Notez que `JSONSerializer` ne sait pas traiter n’importe quel type de donnée Python. Comme c’est souvent le cas, il faut contrebalancer commodité et sécurité. Si vous souhaitez stocker des types de données plus spécialisées comme `datetime` ou `Decimal` dans des sessions en JSON, vous devrez écrire votre propre sérialiseur (ou convertir ces valeurs en objets sérialisables en JSON avant de les stocker dans `request.session`). Bien que la sérialisation de telles valeurs soit relativement directe à réaliser (`DjangoJSONEncoder` donne des pistes), l’écriture d’un décodeur qui puisse reconstruire la valeur initiale de manière fiable est plus délicate. Par exemple, vous risquez de créer un objet `datetime` pour ce qui était en fait une chaîne dont le format ressemblait à celui que vous avez choisi pour représenter les objets `datetime`.

Une classe de sérialisation doit implémenter deux méthodes, `dumps(self, obj)` et `loads(self, data)`, pour respectivement sérialiser et désérialiser le dictionnaire des données de session.

### Lignes directrices des objets sessions

- Utilisez des chaînes Python normales comme clés de dictionnaire pour `request.session`. Il s’agit plus d’une convention que d’une règle stricte.

- Les clés de dictionnaire de session commençant par un soulignement sont réservés pour l’usage interne de Django.

- Ne surchargez pas `request.session` avec un nouvel objet et n’accédez pas ou ne modifiez pas ses attributs. Utilisez-le comme un dictionnaire Python.

### Exemples

Cette vue très simple définit une variable `has_commented` à `True` après qu’un utilisateur a soumis un commentaire. Elle n’autorise pas plus d’un commentaire par utilisateur :

    def post_comment(request, new_comment):
        if request.session.get("has_commented", False):
            return HttpResponse("You've already commented.")
        c = comments.Comment(comment=new_comment)
        c.save()
        request.session["has_commented"] = True
        return HttpResponse("Thanks for your comment!")

Cette vue très simple connecte un « membre » du site :

    def login(request):
        m = Member.objects.get(username=request.POST["username"])
        if m.check_password(request.POST["password"]):
            request.session["member_id"] = m.id
            return HttpResponse("You're logged in.")
        else:
            return HttpResponse("Your username and password didn't match.")

…et celle-ci déconnecte un membre, compte tenu de l’exemple `login()` ci-dessus :

    def logout(request):
        try:
            del request.session["member_id"]
        except KeyError:
            pass
        return HttpResponse("You're logged out.")

La fonction standard `django.contrib.auth.logout()` fait en réalité un peu plus que cela pour empêcher des divulgations de données fortuites. Elle appelle la méthode `flush()` de `request.session`. Nous utilisons cet exemple comme démonstration du fonctionnement des objets sessions, il ne faut pas le considérer comme une implémentation complète de `logout()`.

## Génération de cookies de test

Par commodité, Django fournit un moyen de tester si le navigateur de l’utilisateur accepte les cookies. Appelez la méthode `set_test_cookie()` de `request.session` dans une vue, puis appelez `test_cookie_worked()` dans une des vues suivantes, mais pas dans le même appel de vue.

Cette séparation bizarre entre `set_test_cookie()` et `test_cookie_worked()` est nécessaire en raison du fonctionnement des cookies. Lorsque vous définissez un cookie, il n’est en fait pas possible de savoir si le navigateur l’a accepté avant de recevoir la prochaine requête du navigateur.

It’s good practice to use `delete_test_cookie()` to clean up after yourself. Do this after you’ve verified that the test cookie worked.

Voici un exemple d’utilisation typique :

    from django.http import HttpResponse
    from django.shortcuts import render

    def login(request):
        if request.method == "POST":
            if request.session.test_cookie_worked():
                request.session.delete_test_cookie()
                return HttpResponse("You're logged in.")
            else:
                return HttpResponse("Please enable cookies and try again.")
        request.session.set_test_cookie()
        return render(request, "foo/login_form.html")

## Utilisation des sessions en dehors des vues

Note

Les exemples de cette section importent directement l’objet `SessionStore` à partir du moteur `django.contrib.sessions.backends.db`. Dans votre propre code, il est préférable d’importer `SessionStore` à partir du moteur de sessions désigné par `SESSION_ENGINE`, comme ci-dessous :

    >>> from importlib import import_module
    >>> from django.conf import settings
    >>> SessionStore = import_module(settings.SESSION_ENGINE).SessionStore

Une API est disponible pour manipuler les données de sessions en dehors des vues :

    >>> from django.contrib.sessions.backends.db import SessionStore
    >>> s = SessionStore()
    >>> # stored as seconds since epoch since datetimes are not serializable in JSON.
    >>> s["last_login"] = 1376587691
    >>> s.create()
    >>> s.session_key
    '2b1189a188b44ad18c35e113ac6ceead'
    >>> s = SessionStore(session_key="2b1189a188b44ad18c35e113ac6ceead")
    >>> s["last_login"]
    1376587691

`SessionStore.create()` est conçue pour créer une nouvelle session (c’est-à-dire qui n’est pas chargée du stockage de session et avec `session_key=None`). `save()` est conçue pour enregistrer une session existante (c’est-à-dire qui est chargée du stockage de session). Il est aussi possible d’appeler `save()` pour une nouvelle session, mais avec un petit risque de générer une clé de session qui entre en conflit avec une autre. `create()` appelle `save()` et boucle jusqu’à ce qu’une clé `session_key` inutilisée soit produite.

Si vous utilisez le moteur `django.contrib.sessions.backends.db`, chaque session est un modèle Django normal. Le modèle `Session` est défini dans django/contrib/sessions/models.py. Comme c’est un modèle normal, vous pouvez accéder aux sessions en utilisant l’API habituelle de base de données Django :

    >>> from django.contrib.sessions.models import Session
    >>> s = Session.objects.get(pk="2b1189a188b44ad18c35e113ac6ceead")
    >>> s.expire_date
    datetime.datetime(2005, 8, 20, 13, 35, 12)

Note that you’ll need to call `get_decoded()` to get the session dictionary. This is necessary because the dictionary is stored in an encoded format:

    >>> s.session_data
    'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
    >>> s.get_decoded()
    {'user_id': 42}

## Enregistrement des sessions

Par défaut, Django n’enregistre la session dans la base de données qu’au moment où la session a été modifiée, c’est-à-dire qu’au moins une des valeurs de son dictionnaire a été définie ou supprimée :

    # Session is modified.
    request.session["foo"] = "bar"

    # Session is modified.
    del request.session["foo"]

    # Session is modified.
    request.session["foo"] = {}

    # Gotcha: Session is NOT modified, because this alters
    # request.session['foo'] instead of request.session.
    request.session["foo"]["bar"] = "baz"

Dans le dernier cas de l’exemple ci-dessus, nous pouvons indiquer explicitement à l’objet session qu’il a été modifié en définissant son attribut `modified`:

    request.session.modified = True

Pour modifier ce comportement par défaut, définissez le réglage `SESSION_SAVE_EVERY_REQUEST` à `True`. Dans ce cas, Django enregistre la session dans la base de données lors de chaque requête.

Notez que le cookie de session n’est envoyé que lorsqu’une session a été créée ou modifiée. Si `SESSION_SAVE_EVERY_REQUEST` vaut `True`, le cookie de session sera envoyé lors de chaque requête.

De même, la partie `expires` d’un cookie de session est mis à jour chaque fois que le cookie de session est envoyé.

La session n’est pas enregistrée si le code de statut de la réponse est 500.

## Expiration ou persistance des sessions

Le réglage `SESSION_EXPIRE_AT_BROWSER_CLOSE` permet de contrôler le type des sessions utilisées par l’infrastructure des sessions, soit les sessions expirant à la fermeture du navigateur ou les sessions persistantes.

Par défaut, `SESSION_EXPIRE_AT_BROWSER_CLOSE` est défini à `False`, ce qui signifie que les cookies de session seront stockés dans les navigateurs des utilisateurs pour une durée de `SESSION_COOKIE_AGE`. Utilisé quand on ne souhaite pas que les utilisateurs doivent se connecter chaque fois qu’ils ouvrent leur navigateur.

Si `SESSION_EXPIRE_AT_BROWSER_CLOSE` est défini à `True`, Django utilise des cookies expirant à la fermeture du navigateur. Utilisé quand on souhaite que les utilisateurs doivent se connecter chaque fois qu’ils ouvrent leur navigateur.

Ce réglage est une valeur par défaut globale et peut être surchargé au niveau de chaque session en appelant explicitement la méthode `set_expiry()` de `request.session` comme décrit ci-dessus dans utilisation des sessions dans les vues.

Note

Certains navigateurs (Chrome, par exemple) peuvent être réglés afin de ne pas fermer les sessions de navigation entre fermeture et réouverture du navigateur. Dans certains cas, cela peut interférer avec le réglage `SESSION_EXPIRE_AT_BROWSER_CLOSE` et empêcher les sessions d’expirer à la fermeture du navigateur. Il est important de le savoir lors des tests d’applications Django qui ont le réglage `SESSION_EXPIRE_AT_BROWSER_CLOSE` actif.

## Effacement du stockage des sessions

Au fur et à mesure que des utilisateurs créent de nouvelles sessions sur votre site Web, les données de sessions s’accumulent dans votre stockage de sessions. Si vous utilisez le moteur en base de données, la table de base de données `django_session` prend du volume. Si vous utilisez le moteur basé sur des fichiers, le nombre des fichiers dans le répertoire temporaire va augmenter constamment.

Pour comprendre ce problème, examinons ce qui se passe avec le moteur en base de données. Lorsqu’un utilisateur se connecte, Django ajoute un enregistrement dans la table `django_session`. Il met à jour cet enregistrement chaque fois que les données de la session sont modifiées. Si l’utilisateur se déconnecte explicitement, Django efface l’enregistrement. Mais si l’utilisateur ne le fait pas, l’enregistrement n’est jamais effacé. Le principe est identique avec le moteur basé sur les fichiers.

Django ne fournit pas de système automatique d’effacement des sessions expirées. C’est donc à vous de le faire à intervalle régulier. Django met à disposition une commande de gestion de nettoyage dans ce but : `clearsessions`. Il est recommandé de lancer cette commande régulièrement, par exemple dans une tâche « cron » journalière.

Notez que le moteur basé sur le cache n’est pas sujet à ce problème, car les caches suppriment automatiquement les données obsolètes. Pareil pour le moteur basé sur les cookies, car les données de sessions sont stockées par les navigateurs des utilisateurs.

## Réglages

Quelques réglages Django permettent de contrôler le comportement des sessions :

- `SESSION_CACHE_ALIAS`

- `SESSION_COOKIE_AGE`

- `SESSION_COOKIE_DOMAIN`

- `SESSION_COOKIE_HTTPONLY`

- `SESSION_COOKIE_NAME`

- `SESSION_COOKIE_PATH`

- `SESSION_COOKIE_SAMESITE`

- `SESSION_COOKIE_SECURE`

- `SESSION_ENGINE`

- `SESSION_EXPIRE_AT_BROWSER_CLOSE`

- `SESSION_FILE_PATH`

- `SESSION_SAVE_EVERY_REQUEST`

- `SESSION_SERIALIZER`

## Sécurité des sessions

Les sous-domaines d’un site peuvent définir des cookies pour le client valables pour tout le domaine. Cela rend possible les attaques par fixation de session si des cookies peuvent être créés par des sous-domaines qui ne sont pas sous contrôle de personnes de confiance.

Par exemple, un attaquant pourrait se connecter à `good.example.com` et obtenir une session valable pour son compte. Si l’attaquant contrôle `bad.example.com`, il peut l’utiliser pour vous envoyer sa clé de session puisqu’un sous-domaine a le droit de définir des cookies pour `*.example.com`. Lorsque vous visitez `good.example.com`, vous serez connecté sous le compte de l’attaquant et vous pourriez saisir involontairement des données personnelles sensibles (par ex. des données de carte de crédit) enregistrées dans le compte de l’attaquant.

Une autre attaque possible apparaît dans le cas où `good.example.com` définit son réglage `SESSION_COOKIE_DOMAIN` à `"example.com"`, ce qui pourrait avoir comme conséquence d’envoyer les cookies de session à `bad.example.com`.

## Détails techniques

- Le dictionnaire de session accepte toute valeur sérialisable en `json` quand on utilise `JSONSerializer`.

- Les données des sessions sont stockées dans une table de base de données nommée `django_session` .

- Django n’envoie un cookie qu’en cas de nécessité. Si vous ne définissez pas de données de session, le cookie de session ne sera pas envoyé.

### L’objet `SessionStore`

Lorsque Django manipule les sessions en interne, il emploie un objet de stockage de session provenant du moteur de sessions correspondant. Par convention, la classe d’objet de stockage de session est appelée `SessionStore` et se trouve dans le module désigné par `SESSION_ENGINE`.

Toutes les sous-classes de `SessionStore` disponibles dans Django implémentent les méthodes de manipulation de données suivantes :

- `exists()`

- `create()`

- `save()`

- `delete()`

- `load()`

- `clear_expired()`

Une interface asynchrone de ces méthodes est fournie en les enveloppant dans `sync_to_async()`. Elles peuvent être implémentées directement si une implémentation nativement asynchrone est disponible :

- `aexists()`

- `acreate()`

- `asave()`

- `adelete()`

- `aload()`

- `aclear_expired()`

Dans l’optique de créer un moteur de sessions personnalisé ou d’en adapter un existant, vous pouvez créer une nouvelle classe héritant de `SessionBase` ou de toute autre classe `SessionStore` existante.

Il est possible d’étendre les moteurs de sessions existants, mais pour les moteurs se basant sur une base de données, cela exige généralement un peu plus de travail (lisez la section suivante pour plus de détails).

## Extension des moteurs de sessions s’appuyant sur une base de données

La création d’un moteur de sessions personnalisé s’appuyant sur une base de données en se basant sur ceux qui sont inclus dans Django (c’est-à-dire `db` et `cached_db`) peut se faire en héritant de `AbstractBaseSession` et de l’une des classes `SessionStore`.

`AbstractBaseSession` et `BaseSessionManager` peuvent être importés à partir de `django.contrib.sessions.base_session` afin qu’ils puissent être importés sans devoir inclure `django.contrib.sessions` dans `INSTALLED_APPS`.

class base_session.AbstractBaseSession

  Le modèle abstrait de base d’une session.

  session_key

    La clé primaire. Le champ lui-même peut contenir jusqu’à 40 caractères. L’implémentation actuelle génère une chaîne de 32 caractères (une séquence aléatoire de chiffres et de lettre ASCII minuscules).

  session_data

    Une chaîne contenant un dictionnaire de session codé et sérialisé.

  expire_date

    Une date/heure indiquant la date d’expiration de la session.

    Les sessions qui ont expiré ne sont plus accessibles aux utilisateurs ; elles peuvent tout de même rester stockées dans la base de données jusqu’au lancement de la commande d’administration `clearsessions`.

  classmethod get_session_store_class()

    Renvoie une classe de stockage de session à utiliser avec ce modèle de session.

  get_decoded()

    Renvoie les données de session décodées.

    Le décodage est effectué par la classe de stockage de session.

Vous pouvez aussi personnaliser le gestionnaire du modèle en héritant de `BaseSessionManager`:

class base_session.BaseSessionManager
  encode(session_dict)

    Renvoie le dictionnaire de session indiqué sous forme de chaîne codée et sérialisée.

    Le codage est effectué par la classe de stockage de session en lien à la classe de modèle.

  save(session_key, session_dict, expire_date)

    Enregistre les données de session correspondant à la clé de session indiquée ou supprimer la session dans le cas où les données sont vides.

La personnalisation des classes `SessionStore` se fait en surchargeant les méthodes et les propriétés décrites ci-après :

class backends.db.SessionStore

  Implémentation du stockage de session s’appuyant sur une base de données.

  classmethod get_model_class()

    Surchargez cette méthode afin de renvoyer un modèle de session personnalisé en cas de besoin.

  create_model_instance(data)

    Renvoie une nouvelle instance d’objet modèle de session, qui représente l’état actuel de la session.

    En surchargeant cette méthode, on peut modifier les données du modèle de session avant qu’elles ne soient enregistrées dans la base de données.

class backends.cached_db.SessionStore

  Implémentation du stockage de session s’appuyant sur une base de données et du cache.

  cache_key_prefix

    Un préfixe ajouté à une clé de session pour construire une chaîne de clé de cache.

### Exemple

L’exemple ci-dessous montre un moteur de session personnalisé s’appuyant sur une base de données, incluant une colonne de base de données supplémentaire pour stocker un identifiant de compte (et ainsi fournissant une option pour interroger la base de données pour toutes les sessions actives d’un compte) :

    from django.contrib.sessions.backends.db import SessionStore as DBStore
    from django.contrib.sessions.base_session import AbstractBaseSession
    from django.db import models

    class CustomSession(AbstractBaseSession):
        account_id = models.IntegerField(null=True, db_index=True)

        @classmethod
        def get_session_store_class(cls):
            return SessionStore

    class SessionStore(DBStore):
        @classmethod
        def get_model_class(cls):
            return CustomSession

        def create_model_instance(self, data):
            obj = super().create_model_instance(data)
            try:
                account_id = int(data.get("_auth_user_id"))
            except (ValueError, TypeError):
                account_id = None
            obj.account_id = account_id
            return obj

Si vous faites une migration à partir du stockage de sessions `cached_db` intégré à Django vers un stockage personnalisé basé sur `cached_db`, il est conseillé de surcharger le préfixe de clé de cache afin d’éviter d’éventuels conflits d’espace de noms :

    class SessionStore(CachedDBStore):
        cache_key_prefix = "mysessions.custom_cached_db_backend"

        # ...

## Identifiants de session dans les URL

L’infrastructure des sessions de Django est complètement et uniquement basé sur les cookies. Il ne se rabat pas sur le système des identifiants de session dans les URL comme le fait PHP. C’est une décision conceptuelle volontaire. Cette technique défigure les URL et rend votre site vulnérable aux vols d’identifiants de session par l’intermédiaire de l’en-tête « Referer ».