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).
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_CLASSES contient bien 'django.contrib.sessions.middleware.SessionMiddleware'. Le fichier settings.py créé par défaut par django-admin.py startproject active SessionMiddleware.
Si vous ne souhaitez pas utiliser les sessions, vous pouvez très bien enlever la ligne SessionMiddleware de MIDDLEWARE_CLASSES et la ligne 'django.contrib.sessions' de INSTALLED_APPS. Cela économisera un peu de travail à Django.
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.
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 syncdb pour installer l’unique table de base de données qui stocke les données des sessions.
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 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.
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.
Après avoir configuré le cache, il vous reste deux possibilités pour stocker les données en cache :
Pour obtenir des données de sessions à la fois en cache et persistantes, définir SESSION_ENGINE à "django.contrib.sessions.backends.cached_db". Il s’agit là d’un cache à écriture directe, chaque écriture dans le cache sera également écrite en base de données. Les sessions ne sont lues dans la base de données que si elles ne sont pas déjà dans le cache.
Les deux stockages de sessions sont assez rapides, mais le cache simple est le plus rapide car il ne s’occupe pas de persistance. Dans la plupart des cas, le moteur cached_db sera suffisamment rapide, mais si vous avez besoin de performances optimales et qu’il est acceptable pour vous que les données de sessions soient effacées de temps en temps, le moteur cache est fait pour vous.
Si vous utilisez le moteur de session cached_db, vous devrez aussi suivre les instructions de configuration décrites dans `Utilisation de sessions en base de données`_.
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.
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
It’s recommended to leave the SESSION_COOKIE_HTTPONLY setting on True to prevent access to the stored data from JavaScript.
Avertissement
If the SECRET_KEY is not kept secret and you are using the PickleSerializer, this can lead to arbitrary remote code execution.
An attacker in possession of the SECRET_KEY can not only generate falsified session data, which your site will trust, but also remotely execute arbitrary code, as the data is serialized using pickle.
If you use cookie-based sessions, pay extra care that your secret key is always kept completely secret, for any system which might be remotely accessible.
** 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
Note also that while the MAC can guarantee the authenticity of the data (that it was generated by your site, and not someone else), and the integrity of the data (that it is all there and correct), it cannot guarantee freshness i.e. that you are being sent back the last thing you sent to the client. This means that for some uses of session data, the cookie backend might open you up to replay attacks. Unlike other session backends which keep a server-side record of each session and invalidate it when a user logs out, cookie-based sessions are not invalidated when a user logs out. Thus if an attacker steals a user’s cookie, he can use that cookie to login as that user even if the user logs out. Cookies will only be detected as ‘stale’ if they are older than your SESSION_COOKIE_AGE.
Performance
Finalement, la taille d’un cookie peut influencer la rapidité de votre site.
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.
C’est la classe de base pour tous les objets session. Elle possède les méthodes de dictionnaire standard suivantes :
Exemple :
Exemple : request.session['fav_color'] = 'blue'
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.
Exemple : 'fav_color' in request.session
Exemple : fav_color = request.session.get('fav_color', 'red')
Exemple : fav_color = request.session.pop('fav_color')
Elle possède aussi ces méthodes :
Supprime les données de la session actuelle et régénère la valeur de clé de session qui est renvoyée à l’utilisateur dans le cookie. C’est utile pour s’assurer que les données de session précédentes ne puissent plus être relues par le navigateur de l’utilisateur (par exemple, c’est ce que fait django.contrib.auth.logout()).
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.
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() sur une requête de page précédente. Consultez Génération de cookies de test ci-dessous pour obtenir plus d’informations.
Supprime le cookie de test. Utilisé pour faire le ménage derrière soi.
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 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.
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(), le cas échéant, sinon None.
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 nommés que get_expiry_age().
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.
Supprime les sessions expirées du stockage des sessions. Cette méthode de classe est appelée par clearsessions.
Creates a new session key while retaining the current session data. django.contrib.auth.login() calls this method to mitigate against session fixation.
Before version 1.6, Django defaulted to using pickle to serialize session data before storing it in the backend. If you’re using the signed cookie session backend and SECRET_KEY is known by an attacker (there isn’t an inherent vulnerability in Django that would cause it to leak), the attacker could insert a string into his session which, when unpickled, executes arbitrary code on the server. The technique for doing so is simple and easily available on the internet. Although the cookie session storage signs the cookie-stored data to prevent tampering, a SECRET_KEY leak immediately escalates to a remote code execution vulnerability.
This attack can be mitigated by serializing session data using JSON rather than pickle. To facilitate this, Django 1.5.3 introduced a new setting, SESSION_SERIALIZER, to customize the session serialization format. For backwards compatibility, this setting defaults to using django.contrib.sessions.serializers.PickleSerializer in Django 1.5.x, but, for security hardening, defaults to django.contrib.sessions.serializers.JSONSerializer in Django 1.6. If you upgrade and switch from pickle to JSON, sessions created before the upgrade will be lost. Even with the caveats described in Write Your Own Serializer, we highly recommend using JSON serialization especially if you are using the cookie backend.
A wrapper around the JSON serializer from django.core.signing. Can only serialize basic data types.
In addition, as JSON supports only string keys, note that using non-string keys in request.session won’t work as expected:
>>> # initial assignment
>>> request.session[0] = 'bar'
>>> # subsequent requests following serialization & deserialization
>>> # of session data
>>> request.session[0] # KeyError
>>> request.session['0']
'bar'
See the Write Your Own Serializer section for more details on limitations of JSON serialization.
Supports arbitrary Python objects, but, as described above, can lead to a remote code execution vulnerability if SECRET_KEY becomes known by an attacker.
Note that unlike PickleSerializer, the JSONSerializer cannot handle arbitrary Python data types. As is often the case, there is a trade-off between convenience and security. If you wish to store more advanced data types including datetime and Decimal in JSON backed sessions, you will need to write a custom serializer (or convert such values to a JSON serializable object before storign them in request.session). While serializing these values is fairly straightforward (django.core.serializers.json.DateTimeAwareJSONEncoder may be helpful), writing a decoder that can reliably get back the same thing that you put in is more fragile. For example, you run the risk of returning a datetime that was actually a string that just happened to be in the same format chosen for datetimes).
Your serializer class must implement two methods, dumps(self, obj) and loads(self, data), to serialize and deserialize the dictionary of session data, respectively.
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.
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.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().
Par commodité, Django fournit un moyen simple de tester si le navigateur de l’utilisateur accepte les cookies. Il suffit d’appeler la méthode set_test_cookie() de request.session dans une vue, et d’appeler 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.
Il est recommandé d’utiliser delete_test_cookie() pour faire le ménage derrière soi. Faites-le après avoir vérifié que le cookie de test a fonctionné.
Voici un exemple d’utilisation typique :
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_to_response('foo/login_form.html')
Une API est disponible pour manipuler les données de sessions en dehors des vues :
>>> from django.contrib.sessions.backends.db import SessionStore
>>> import datetime
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s['last_login'] = 1376587691
>>> s.save()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login']
1376587691
In order to mitigate session fixation attacks, sessions keys that don’t exist are regenerated:
>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore(session_key='no-such-session-here')
>>> s.save()
>>> s.session_key
'ff882814010ccbc3c870523934fee5a2'
Si vous utilisez le moteur django.contrib.sessions.backends.db, chaque session n’est en fait qu’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)
Notez que vous devrez appeler get_decoded() pour obtenir le dictionnaire de session. Cela est nécessaire car le dictionnaire est stocké dans un format codé :
>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}
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é.
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.
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.
Quelques réglages Django permettent de contrôler le comportement des sessions :
Valeur par défaut : django.contrib.sessions.backends.db
Contrôle l’emplacement de stockage des données de sessions. Les valeurs possibles sont :
Consultez la configuration du moteur de sessions pour plus de détails.
Valeur par défaut : /tmp/
Si vous stockez les sessions dans des fichiers, ce réglage détermine le répertoire dans lequel Django stocke les données des sessions.
Valeur par défaut : 1209600 (2 semaines, en secondes)
L’âge des cookies de sessions, en secondes.
Valeur par défaut : None
Le domaine à utiliser pour les cookies de session. Pour des cookies inter-domaines, utilisez une syntaxe du genre ".example.com" (remarquez le point initial !) et laissez la valeur None pour des cookies de domaine standards.
Valeur par défaut : True
Indique s’il faut définir le drapeau HTTPOnly sur le cookie de session. Si défini à True, le JavaScript côté client ne sera pas autorisé à accéder au cookie de session.
HTTPOnly est un drapeau inclus dans un en-tête de réponse HTTP Set-Cookie. Il ne fait pas partie du standard RFC 2109 pour les cookies, et les navigateurs n’en tiennent pas tous compte. Cependant, quand il est pris en compte, c’est un moyen utile de réduire le risque d’un script côté client accédant les données d’un cookie protégé.
Valeur par défaut : 'sessionid'
Le nom du cookie à utiliser pour les sessions. Vous pouvez y mettre ce qui vous plaît.
Valeur par défaut : '/'
Le chemin défini dans le cookie de session. Cela doit correspondre au chemin d’URL de votre installation de Django ou être un parent de ce chemin.
Utile si vous avez plusieurs instances de Django fonctionnant sous le même nom d’hôte. Ils peuvent alors utiliser des chemins de cookies différents pour que chaque instance ne voit que son propre cookie de session.
Valeur par défaut :
Indique s’il est nécessaire d’utiliser un cookie sécurisé pour le cookie de session. Si défini à True, le cookie sera marqué comme « sécurisé », ce qui signifie que les navigateurs sont chargés de vérifier que le cookie n’est envoyé que pour des connexions HTTPS.
Valeur par défaut :
Indique si la session doit expirer au moment où l’utilisateur ferme son navigateur. Voir « Expiration ou persistance des sessions » ci-dessus.
Valeur par défaut :
Indique si les données de sessions sont enregistrées lors de chaque requête. Avec la valeur False (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.
Subdomains within a site are able to set cookies on the client for the whole domain. This makes session fixation possible if cookies are permitted from subdomains not controlled by trusted users.
For example, an attacker could log into good.example.com and get a valid session for his account. If the attacker has control over bad.example.com, he can use it to send his session key to you since a subdomain is permitted to set cookies on *.example.com. When you visit good.example.com, you’ll be logged in as the attacker and might inadvertently enter your sensitive personal data (e.g. credit card info) into the attackers account.
Another possible attack would be if good.example.com sets its SESSION_COOKIE_DOMAIN to ".example.com" which would cause session cookies from that site to be sent to bad.example.com.
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’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 ».
Jan 13, 2016