1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015 |
- ===================
- How to use sessions
- ===================
- .. module:: django.contrib.sessions
- :synopsis: Provides session management for Django projects.
- Django provides full support for anonymous sessions. The session framework
- lets you store and retrieve arbitrary data on a per-site-visitor basis. It
- stores data on the server side and abstracts the sending and receiving of
- cookies. Cookies contain a session ID -- not the data itself (unless you're
- using the :ref:`cookie based backend<cookie-session-backend>`).
- Enabling sessions
- =================
- Sessions are implemented via a piece of :doc:`middleware </ref/middleware>`.
- To enable session functionality, do the following:
- * Edit the :setting:`MIDDLEWARE` setting and make sure it contains
- ``'django.contrib.sessions.middleware.SessionMiddleware'``. The default
- ``settings.py`` created by ``django-admin startproject`` has
- ``SessionMiddleware`` activated.
- If you don't want to use sessions, you might as well remove the
- ``SessionMiddleware`` line from :setting:`MIDDLEWARE` and
- ``'django.contrib.sessions'`` from your :setting:`INSTALLED_APPS`.
- It'll save you a small bit of overhead.
- .. _configuring-sessions:
- Configuring the session engine
- ==============================
- By default, Django stores sessions in your database (using the model
- ``django.contrib.sessions.models.Session``). Though this is convenient, in
- some setups it's faster to store session data elsewhere, so Django can be
- configured to store session data on your filesystem or in your cache.
- Using database-backed sessions
- ------------------------------
- If you want to use a database-backed session, you need to add
- ``'django.contrib.sessions'`` to your :setting:`INSTALLED_APPS` setting.
- Once you have configured your installation, run ``manage.py migrate``
- to install the single database table that stores session data.
- .. _cached-sessions-backend:
- Using cached sessions
- ---------------------
- For better performance, you may want to use a cache-based session backend.
- To store session data using Django's cache system, you'll first need to make
- sure you've configured your cache; see the :doc:`cache documentation
- </topics/cache>` for details.
- .. warning::
- You should only use cache-based sessions if you're using the Memcached or
- Redis cache backend. The local-memory cache backend doesn't retain data
- long enough to be a good choice, and it'll be faster to use file or
- database sessions directly instead of sending everything through the file
- or database cache backends. Additionally, the local-memory cache backend is
- NOT multi-process safe, therefore probably not a good choice for production
- environments.
- If you have multiple caches defined in :setting:`CACHES`, Django will use the
- default cache. To use another cache, set :setting:`SESSION_CACHE_ALIAS` to the
- name of that cache.
- Once your cache is configured, you have to choose between a database-backed
- cache or a non-persistent cache.
- The cached database backend (``cached_db``) uses a write-through cache --
- session writes are applied to both the database and cache, in that order. If
- writing to the cache fails, the exception is handled and logged via the
- :ref:`sessions logger <django-contrib-sessions-logger>`, to avoid failing an
- otherwise successful write operation.
- .. versionchanged:: 5.1
- Handling and logging of exceptions when writing to the cache was added.
- Session reads use the cache, or the database if the data has been evicted from
- the cache. To use this backend, set :setting:`SESSION_ENGINE` to
- ``"django.contrib.sessions.backends.cached_db"``, and follow the configuration
- instructions for the `using database-backed sessions`_.
- The cache backend (``cache``) stores session data only in your cache. This is
- faster because it avoids database persistence, but you will have to consider
- what happens when cache data is evicted. Eviction can occur if the cache fills
- up or the cache server is restarted, and it will mean session data is lost,
- including logging out users. To use this backend, set :setting:`SESSION_ENGINE`
- to ``"django.contrib.sessions.backends.cache"``.
- The cache backend can be made persistent by using a persistent cache, such as
- Redis with appropriate configuration. But unless your cache is definitely
- configured for sufficient persistence, opt for the cached database backend.
- This avoids edge cases caused by unreliable data storage in production.
- Using file-based sessions
- -------------------------
- To use file-based sessions, set the :setting:`SESSION_ENGINE` setting to
- ``"django.contrib.sessions.backends.file"``.
- You might also want to set the :setting:`SESSION_FILE_PATH` setting (which
- defaults to output from ``tempfile.gettempdir()``, most likely ``/tmp``) to
- control where Django stores session files. Be sure to check that your web
- server has permissions to read and write to this location.
- .. _cookie-session-backend:
- Using cookie-based sessions
- ---------------------------
- To use cookies-based sessions, set the :setting:`SESSION_ENGINE` setting to
- ``"django.contrib.sessions.backends.signed_cookies"``. The session data will be
- stored using Django's tools for :doc:`cryptographic signing </topics/signing>`
- and the :setting:`SECRET_KEY` setting.
- .. note::
- It's recommended to leave the :setting:`SESSION_COOKIE_HTTPONLY` setting
- on ``True`` to prevent access to the stored data from JavaScript.
- .. warning::
- **The session data is signed but not encrypted**
- When using the cookies backend the session data can be read by the client.
- A MAC (Message Authentication Code) is used to protect the data against
- changes by the client, so that the session data will be invalidated when being
- tampered with. The same invalidation happens if the client storing the
- cookie (e.g. your user's browser) can't store all of the session cookie and
- drops data. Even though Django compresses the data, it's still entirely
- possible to exceed the :rfc:`common limit of 4096 bytes <2965#section-5.3>`
- per cookie.
- **No freshness guarantee**
- 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, they 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
- :setting:`SESSION_COOKIE_AGE`.
- **Performance**
- Finally, the size of a cookie can have an impact on the speed of your site.
- .. _`replay attacks`: https://en.wikipedia.org/wiki/Replay_attack
- Using sessions in views
- =======================
- When ``SessionMiddleware`` is activated, each :class:`~django.http.HttpRequest`
- object -- the first argument to any Django view function -- will have a
- ``session`` attribute, which is a dictionary-like object.
- You can read it and write to ``request.session`` at any point in your view.
- You can edit it multiple times.
- .. class:: backends.base.SessionBase
- This is the base class for all session objects. It has the following
- standard dictionary methods:
- .. method:: __getitem__(key)
- Example: ``fav_color = request.session['fav_color']``
- .. method:: __setitem__(key, value)
- Example: ``request.session['fav_color'] = 'blue'``
- .. method:: __delitem__(key)
- Example: ``del request.session['fav_color']``. This raises ``KeyError``
- if the given ``key`` isn't already in the session.
- .. method:: __contains__(key)
- Example: ``'fav_color' in request.session``
- .. method:: get(key, default=None)
- .. method:: aget(key, default=None)
- *Asynchronous version*: ``aget()``
- Example: ``fav_color = request.session.get('fav_color', 'red')``
- .. versionchanged:: 5.1
- ``aget()`` function was added.
- .. method:: aset(key, value)
- .. versionadded:: 5.1
- Example: ``await request.session.aset('fav_color', 'red')``
- .. method:: update(dict)
- .. method:: aupdate(dict)
- *Asynchronous version*: ``aupdate()``
- Example: ``request.session.update({'fav_color': 'red'})``
- .. versionchanged:: 5.1
- ``aupdate()`` function was added.
- .. method:: pop(key, default=__not_given)
- .. method:: apop(key, default=__not_given)
- *Asynchronous version*: ``apop()``
- Example: ``fav_color = request.session.pop('fav_color', 'blue')``
- .. versionchanged:: 5.1
- ``apop()`` function was added.
- .. method:: keys()
- .. method:: akeys()
- *Asynchronous version*: ``akeys()``
- .. versionchanged:: 5.1
- ``akeys()`` function was added.
- .. method:: values()
- .. method:: avalues()
- *Asynchronous version*: ``avalues()``
- .. versionchanged:: 5.1
- ``avalues()`` function was added.
- .. method:: has_key(key)
- .. method:: ahas_key(key)
- *Asynchronous version*: ``ahas_key()``
- .. versionchanged:: 5.1
- ``ahas_key()`` function was added.
- .. method:: items()
- .. method:: aitems()
- *Asynchronous version*: ``aitems()``
- .. versionchanged:: 5.1
- ``aitems()`` function was added.
- .. method:: setdefault()
- .. method:: asetdefault()
- *Asynchronous version*: ``asetdefault()``
- .. versionchanged:: 5.1
- ``asetdefault()`` function was added.
- .. method:: clear()
- It also has these methods:
- .. method:: flush()
- .. method:: aflush()
- *Asynchronous version*: ``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
- :func:`django.contrib.auth.logout()` function calls it).
- .. versionchanged:: 5.1
- ``aflush()`` function was added.
- .. method:: set_test_cookie()
- .. method:: aset_test_cookie()
- *Asynchronous version*: ``aset_test_cookie()``
- Sets a test cookie to determine whether the user's browser supports
- cookies. Due to the way cookies work, you won't be able to test this
- until the user's next page request. See `Setting test cookies`_ below for
- more information.
- .. versionchanged:: 5.1
- ``aset_test_cookie()`` function was added.
- .. method:: test_cookie_worked()
- .. method:: atest_cookie_worked()
- *Asynchronous version*: ``atest_cookie_worked()``
- Returns either ``True`` or ``False``, depending on whether the user's
- browser accepted the test cookie. Due to the way cookies work, you'll
- have to call ``set_test_cookie()`` or ``aset_test_cookie()`` on a
- previous, separate page request.
- See `Setting test cookies`_ below for more information.
- .. versionchanged:: 5.1
- ``atest_cookie_worked()`` function was added.
- .. method:: delete_test_cookie()
- .. method:: adelete_test_cookie()
- *Asynchronous version*: ``adelete_test_cookie()``
- Deletes the test cookie. Use this to clean up after yourself.
- .. versionchanged:: 5.1
- ``adelete_test_cookie()`` function was added.
- .. method:: get_session_cookie_age()
- Returns the value of the setting :setting:`SESSION_COOKIE_AGE`. This can
- be overridden in a custom session backend.
- .. method:: set_expiry(value)
- .. method:: aset_expiry(value)
- *Asynchronous version*: ``aset_expiry()``
- Sets the expiration time for the session. You can pass a number of
- different values:
- * If ``value`` is an integer, the session will expire after that
- many seconds of inactivity. For example, calling
- ``request.session.set_expiry(300)`` would make the session expire
- in 5 minutes.
- * If ``value`` is a ``datetime`` or ``timedelta`` object, the session
- will expire at that specific date/time.
- * If ``value`` is ``0``, the user's session cookie will expire
- when the user's web browser is closed.
- * If ``value`` is ``None``, the session reverts to using the global
- session expiry policy.
- Reading a session is not considered activity for expiration
- purposes. Session expiration is computed from the last time the
- session was *modified*.
- .. versionchanged:: 5.1
- ``aset_expiry()`` function was added.
- .. method:: get_expiry_age()
- .. method:: aget_expiry_age()
- *Asynchronous version*: ``aget_expiry_age()``
- Returns the number of seconds until this session expires. For sessions
- with no custom expiration (or those set to expire at browser close), this
- will equal :setting:`SESSION_COOKIE_AGE`.
- This function accepts two optional keyword arguments:
- - ``modification``: last modification of the session, as a
- :class:`~datetime.datetime` object. Defaults to the current time.
- - ``expiry``: expiry information for the session, as a
- :class:`~datetime.datetime` object, an :class:`int` (in seconds), or
- ``None``. Defaults to the value stored in the session by
- :meth:`set_expiry`/:meth:`aset_expiry`, if there is one, or ``None``.
- .. note::
- This method is used by session backends to determine the session expiry
- age in seconds when saving the session. It is not really intended for
- usage outside of that context.
- In particular, while it is **possible** to determine the remaining
- lifetime of a session **just when** you have the correct
- ``modification`` value **and** the ``expiry`` is set as a ``datetime``
- object, where you do have the ``modification`` value, it is more
- straight-forward to calculate the expiry by-hand::
- expires_at = modification + timedelta(seconds=settings.SESSION_COOKIE_AGE)
- .. versionchanged:: 5.1
- ``aget_expiry_age()`` function was added.
- .. method:: get_expiry_date()
- .. method:: aget_expiry_date()
- *Asynchronous version*: ``aget_expiry_date()``
- Returns the date this session will expire. For sessions with no custom
- expiration (or those set to expire at browser close), this will equal the
- date :setting:`SESSION_COOKIE_AGE` seconds from now.
- This function accepts the same keyword arguments as
- :meth:`get_expiry_age`, and similar notes on usage apply.
- .. versionchanged:: 5.1
- ``aget_expiry_date()`` function was added.
- .. method:: get_expire_at_browser_close()
- .. method:: aget_expire_at_browser_close()
- *Asynchronous version*: ``aget_expire_at_browser_close()``
- Returns either ``True`` or ``False``, depending on whether the user's
- session cookie will expire when the user's web browser is closed.
- .. versionchanged:: 5.1
- ``aget_expire_at_browser_close()`` function was added.
- .. method:: clear_expired()
- .. method:: aclear_expired()
- *Asynchronous version*: ``aclear_expired()``
- Removes expired sessions from the session store. This class method is
- called by :djadmin:`clearsessions`.
- .. versionchanged:: 5.1
- ``aclear_expired()`` function was added.
- .. method:: cycle_key()
- .. method:: acycle_key()
- *Asynchronous version*: ``acycle_key()``
- Creates a new session key while retaining the current session data.
- :func:`django.contrib.auth.login()` calls this method to mitigate against
- session fixation.
- .. versionchanged:: 5.1
- ``acycle_key()`` function was added.
- .. _session_serialization:
- Session serialization
- ---------------------
- By default, Django serializes session data using JSON. You can use the
- :setting:`SESSION_SERIALIZER` setting to customize the session serialization
- format. Even with the caveats described in :ref:`custom-serializers`, we highly
- recommend sticking with JSON serialization *especially if you are using the
- cookie backend*.
- For example, here's an attack scenario if you use :mod:`pickle` to serialize
- session data. If you're using the :ref:`signed cookie session backend
- <cookie-session-backend>` and :setting:`SECRET_KEY` (or any key of
- :setting:`SECRET_KEY_FALLBACKS`) 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 their 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 :setting:`SECRET_KEY` leak
- immediately escalates to a remote code execution vulnerability.
- Bundled serializers
- ~~~~~~~~~~~~~~~~~~~
- .. class:: serializers.JSONSerializer
- A wrapper around the JSON serializer from :mod:`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:
- .. code-block:: pycon
- >>> # initial assignment
- >>> request.session[0] = "bar"
- >>> # subsequent requests following serialization & deserialization
- >>> # of session data
- >>> request.session[0] # KeyError
- >>> request.session["0"]
- 'bar'
- Similarly, data that can't be encoded in JSON, such as non-UTF8 bytes like
- ``'\xd9'`` (which raises :exc:`UnicodeDecodeError`), can't be stored.
- See the :ref:`custom-serializers` section for more details on limitations
- of JSON serialization.
- .. _custom-serializers:
- Write your own serializer
- ~~~~~~~~~~~~~~~~~~~~~~~~~
- Note that the :class:`~django.contrib.sessions.serializers.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 storing them in ``request.session``). While
- serializing these values is often straightforward
- (:class:`~django.core.serializers.json.DjangoJSONEncoder` 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
- ``datetime``\s).
- Your serializer class must implement two methods,
- ``dumps(self, obj)`` and ``loads(self, data)``, to serialize and deserialize
- the dictionary of session data, respectively.
- Session object guidelines
- -------------------------
- * Use normal Python strings as dictionary keys on ``request.session``. This
- is more of a convention than a hard-and-fast rule.
- * Session dictionary keys that begin with an underscore are reserved for
- internal use by Django.
- * Don't override ``request.session`` with a new object, and don't access or
- set its attributes. Use it like a Python dictionary.
- Examples
- --------
- This simplistic view sets a ``has_commented`` variable to ``True`` after a user
- posts a comment. It doesn't let a user post a comment more than once::
- 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!")
- This simplistic view logs in a "member" of the 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.")
- ...And this one logs a member out, according to ``login()`` above::
- def logout(request):
- try:
- del request.session["member_id"]
- except KeyError:
- pass
- return HttpResponse("You're logged out.")
- The standard :meth:`django.contrib.auth.logout` function actually does a bit
- more than this to prevent inadvertent data leakage. It calls the
- :meth:`~backends.base.SessionBase.flush` method of ``request.session``.
- We are using this example as a demonstration of how to work with session
- objects, not as a full ``logout()`` implementation.
- Setting test cookies
- ====================
- As a convenience, Django provides a way to test whether the user's browser
- accepts cookies. Call the :meth:`~backends.base.SessionBase.set_test_cookie`
- method of ``request.session`` in a view, and call
- :meth:`~backends.base.SessionBase.test_cookie_worked` in a subsequent view --
- not in the same view call.
- This awkward split between ``set_test_cookie()`` and ``test_cookie_worked()``
- is necessary due to the way cookies work. When you set a cookie, you can't
- actually tell whether a browser accepted it until the browser's next request.
- It's good practice to use
- :meth:`~backends.base.SessionBase.delete_test_cookie()` to clean up after
- yourself. Do this after you've verified that the test cookie worked.
- Here's a typical usage example::
- 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")
- .. versionchanged:: 5.1
- Support for setting test cookies in asynchronous view functions was added.
- Using sessions out of views
- ===========================
- .. note::
- The examples in this section import the ``SessionStore`` object directly
- from the ``django.contrib.sessions.backends.db`` backend. In your own code,
- you should consider importing ``SessionStore`` from the session engine
- designated by :setting:`SESSION_ENGINE`, as below:
- .. code-block:: pycon
- >>> from importlib import import_module
- >>> from django.conf import settings
- >>> SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
- An API is available to manipulate session data outside of a view:
- .. code-block:: pycon
- >>> 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()`` is designed to create a new session (i.e. one not
- loaded from the session store and with ``session_key=None``). ``save()`` is
- designed to save an existing session (i.e. one loaded from the session store).
- Calling ``save()`` on a new session may also work but has a small chance of
- generating a ``session_key`` that collides with an existing one. ``create()``
- calls ``save()`` and loops until an unused ``session_key`` is generated.
- If you're using the ``django.contrib.sessions.backends.db`` backend, each
- session is a normal Django model. The ``Session`` model is defined in
- :source:`django/contrib/sessions/models.py`. Because it's a normal model, you can
- access sessions using the normal Django database API:
- .. code-block:: pycon
- >>> 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
- :meth:`~base_session.AbstractBaseSession.get_decoded()` to get the session
- dictionary. This is necessary because the dictionary is stored in an encoded
- format:
- .. code-block:: pycon
- >>> s.session_data
- 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
- >>> s.get_decoded()
- {'user_id': 42}
- When sessions are saved
- =======================
- By default, Django only saves to the session database when the session has been
- modified -- that is if any of its dictionary values have been assigned or
- deleted::
- # 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"
- In the last case of the above example, we can tell the session object
- explicitly that it has been modified by setting the ``modified`` attribute on
- the session object::
- request.session.modified = True
- To change this default behavior, set the :setting:`SESSION_SAVE_EVERY_REQUEST`
- setting to ``True``. When set to ``True``, Django will save the session to the
- database on every single request.
- Note that the session cookie is only sent when a session has been created or
- modified. If :setting:`SESSION_SAVE_EVERY_REQUEST` is ``True``, the session
- cookie will be sent on every request.
- Similarly, the ``expires`` part of a session cookie is updated each time the
- session cookie is sent.
- The session is not saved if the response's status code is 500.
- .. _browser-length-vs-persistent-sessions:
- Browser-length sessions vs. persistent sessions
- ===============================================
- You can control whether the session framework uses browser-length sessions vs.
- persistent sessions with the :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE`
- setting.
- By default, :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` is set to ``False``,
- which means session cookies will be stored in users' browsers for as long as
- :setting:`SESSION_COOKIE_AGE`. Use this if you don't want people to have to
- log in every time they open a browser.
- If :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` is set to ``True``, Django will
- use browser-length cookies -- cookies that expire as soon as the user closes
- their browser. Use this if you want people to have to log in every time they
- open a browser.
- This setting is a global default and can be overwritten at a per-session level
- by explicitly calling the :meth:`~backends.base.SessionBase.set_expiry` method
- of ``request.session`` as described above in `using sessions in views`_.
- .. note::
- Some browsers (Chrome, for example) provide settings that allow users to
- continue browsing sessions after closing and reopening the browser. In
- some cases, this can interfere with the
- :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` setting and prevent sessions
- from expiring on browser close. Please be aware of this while testing
- Django applications which have the
- :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` setting enabled.
- .. _clearing-the-session-store:
- Clearing the session store
- ==========================
- As users create new sessions on your website, session data can accumulate in
- your session store. If you're using the database backend, the
- ``django_session`` database table will grow. If you're using the file backend,
- your temporary directory will contain an increasing number of files.
- To understand this problem, consider what happens with the database backend.
- When a user logs in, Django adds a row to the ``django_session`` database
- table. Django updates this row each time the session data changes. If the user
- logs out manually, Django deletes the row. But if the user does *not* log out,
- the row never gets deleted. A similar process happens with the file backend.
- Django does *not* provide automatic purging of expired sessions. Therefore,
- it's your job to purge expired sessions on a regular basis. Django provides a
- clean-up management command for this purpose: :djadmin:`clearsessions`. It's
- recommended to call this command on a regular basis, for example as a daily
- cron job.
- Note that the cache backend isn't vulnerable to this problem, because caches
- automatically delete stale data. Neither is the cookie backend, because the
- session data is stored by the users' browsers.
- Settings
- ========
- A few :ref:`Django settings <settings-sessions>` give you control over session
- behavior:
- * :setting:`SESSION_CACHE_ALIAS`
- * :setting:`SESSION_COOKIE_AGE`
- * :setting:`SESSION_COOKIE_DOMAIN`
- * :setting:`SESSION_COOKIE_HTTPONLY`
- * :setting:`SESSION_COOKIE_NAME`
- * :setting:`SESSION_COOKIE_PATH`
- * :setting:`SESSION_COOKIE_SAMESITE`
- * :setting:`SESSION_COOKIE_SECURE`
- * :setting:`SESSION_ENGINE`
- * :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE`
- * :setting:`SESSION_FILE_PATH`
- * :setting:`SESSION_SAVE_EVERY_REQUEST`
- * :setting:`SESSION_SERIALIZER`
- .. _topics-session-security:
- Session security
- ================
- 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 their account. If the attacker has control over ``bad.example.com``,
- they can use it to send their 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 attacker's account.
- Another possible attack would be if ``good.example.com`` sets its
- :setting:`SESSION_COOKIE_DOMAIN` to ``"example.com"`` which would cause
- session cookies from that site to be sent to ``bad.example.com``.
- Technical details
- =================
- * The session dictionary accepts any :mod:`json` serializable value when using
- :class:`~django.contrib.sessions.serializers.JSONSerializer`.
- * Session data is stored in a database table named ``django_session`` .
- * Django only sends a cookie if it needs to. If you don't set any session
- data, it won't send a session cookie.
- The ``SessionStore`` object
- ---------------------------
- When working with sessions internally, Django uses a session store object from
- the corresponding session engine. By convention, the session store object class
- is named ``SessionStore`` and is located in the module designated by
- :setting:`SESSION_ENGINE`.
- All ``SessionStore`` subclasses available in Django implement the following
- data manipulation methods:
- * ``exists()``
- * ``create()``
- * ``save()``
- * ``delete()``
- * ``load()``
- * :meth:`~.SessionBase.clear_expired`
- An asynchronous interface for these methods is provided by wrapping them with
- ``sync_to_async()``. They can be implemented directly if an async-native
- implementation is available:
- * ``aexists()``
- * ``acreate()``
- * ``asave()``
- * ``adelete()``
- * ``aload()``
- * :meth:`~.SessionBase.aclear_expired`
- In order to build a custom session engine or to customize an existing one, you
- may create a new class inheriting from :class:`~backends.base.SessionBase` or
- any other existing ``SessionStore`` class.
- You can extend the session engines, but doing so with database-backed session
- engines generally requires some extra effort (see the next section for
- details).
- .. versionchanged:: 5.1
- ``aexists()``, ``acreate()``, ``asave()``, ``adelete()``, ``aload()``, and
- ``aclear_expired()`` methods were added.
- .. _extending-database-backed-session-engines:
- Extending database-backed session engines
- =========================================
- Creating a custom database-backed session engine built upon those included in
- Django (namely ``db`` and ``cached_db``) may be done by inheriting
- :class:`~base_session.AbstractBaseSession` and either ``SessionStore`` class.
- ``AbstractBaseSession`` and ``BaseSessionManager`` are importable from
- ``django.contrib.sessions.base_session`` so that they can be imported without
- including ``django.contrib.sessions`` in :setting:`INSTALLED_APPS`.
- .. class:: base_session.AbstractBaseSession
- The abstract base session model.
- .. attribute:: session_key
- Primary key. The field itself may contain up to 40 characters. The
- current implementation generates a 32-character string (a random
- sequence of digits and lowercase ASCII letters).
- .. attribute:: session_data
- A string containing an encoded and serialized session dictionary.
- .. attribute:: expire_date
- A datetime designating when the session expires.
- Expired sessions are not available to a user, however, they may still
- be stored in the database until the :djadmin:`clearsessions` management
- command is run.
- .. classmethod:: get_session_store_class()
- Returns a session store class to be used with this session model.
- .. method:: get_decoded()
- Returns decoded session data.
- Decoding is performed by the session store class.
- You can also customize the model manager by subclassing
- :class:`~django.contrib.sessions.base_session.BaseSessionManager`:
- .. class:: base_session.BaseSessionManager
- .. method:: encode(session_dict)
- Returns the given session dictionary serialized and encoded as a string.
- Encoding is performed by the session store class tied to a model class.
- .. method:: save(session_key, session_dict, expire_date)
- Saves session data for a provided session key, or deletes the session
- in case the data is empty.
- Customization of ``SessionStore`` classes is achieved by overriding methods
- and properties described below:
- .. class:: backends.db.SessionStore
- Implements database-backed session store.
- .. classmethod:: get_model_class()
- Override this method to return a custom session model if you need one.
- .. method:: create_model_instance(data)
- Returns a new instance of the session model object, which represents
- the current session state.
- Overriding this method provides the ability to modify session model
- data before it's saved to database.
- .. class:: backends.cached_db.SessionStore
- Implements cached database-backed session store.
- .. attribute:: cache_key_prefix
- A prefix added to a session key to build a cache key string.
- Example
- -------
- The example below shows a custom database-backed session engine that includes
- an additional database column to store an account ID (thus providing an option
- to query the database for all active sessions for an account)::
- 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
- If you are migrating from the Django's built-in ``cached_db`` session store to
- a custom one based on ``cached_db``, you should override the cache key prefix
- in order to prevent a namespace clash::
- class SessionStore(CachedDBStore):
- cache_key_prefix = "mysessions.custom_cached_db_backend"
- # ...
- Session IDs in URLs
- ===================
- The Django sessions framework is entirely, and solely, cookie-based. It does
- not fall back to putting session IDs in URLs as a last resort, as PHP does.
- This is an intentional design decision. Not only does that behavior make URLs
- ugly, it makes your site vulnerable to session-ID theft via the "Referer"
- header.
|