mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			692 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			692 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ===================
 | ||
| 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_CLASSES` 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_CLASSES` 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
 | ||
|     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've got two choices for how to store data in
 | ||
| the cache:
 | ||
| 
 | ||
| * Set :setting:`SESSION_ENGINE` to
 | ||
|   ``"django.contrib.sessions.backends.cache"`` for a simple caching session
 | ||
|   store. Session data will be stored directly in your cache. However, session
 | ||
|   data may not be persistent: cached data can be evicted if the cache fills
 | ||
|   up or if the cache server is restarted.
 | ||
| 
 | ||
| * For persistent, cached data, set :setting:`SESSION_ENGINE` to
 | ||
|   ``"django.contrib.sessions.backends.cached_db"``. This uses a
 | ||
|   write-through cache -- every write to the cache will also be written to
 | ||
|   the database. Session reads only use the database if the data is not
 | ||
|   already in the cache.
 | ||
| 
 | ||
| Both session stores are quite fast, but the simple cache is faster because it
 | ||
| disregards persistence. In most cases, the ``cached_db`` backend will be fast
 | ||
| enough, but if you need that last bit of performance, and are willing to let
 | ||
| session data be expunged from time to time, the ``cache`` backend is for you.
 | ||
| 
 | ||
| If you use the ``cached_db`` session backend, you also need to follow the
 | ||
| configuration instructions for the `using database-backed sessions`_.
 | ||
| 
 | ||
| .. versionchanged:: 1.7
 | ||
| 
 | ||
|     Before version 1.7, the ``cached_db`` backend always used the ``default``
 | ||
|     cache rather than the :setting:`SESSION_CACHE_ALIAS`.
 | ||
| 
 | ||
| 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::
 | ||
| 
 | ||
|     **If the SECRET_KEY is not kept secret and you are using the**
 | ||
|     :class:`~django.contrib.sessions.serializers.PickleSerializer`, **this can
 | ||
|     lead to arbitrary remote code execution.**
 | ||
| 
 | ||
|     An attacker in possession of the :setting:`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.
 | ||
| 
 | ||
|     **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 `common limit of 4096 bytes`_ 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`_.
 | ||
| 
 | ||
| .. _`common limit of 4096 bytes`: http://tools.ietf.org/html/rfc2965#section-5.3
 | ||
| .. _`replay attacks`: https://en.wikipedia.org/wiki/Replay_attack
 | ||
| .. _`speed of your site`: http://yuiblog.com/blog/2007/03/01/performance-research-part-3/
 | ||
| 
 | ||
| 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)
 | ||
| 
 | ||
|       Example: ``fav_color = request.session.get('fav_color', 'red')``
 | ||
| 
 | ||
|     .. method:: pop(key)
 | ||
| 
 | ||
|       Example: ``fav_color = request.session.pop('fav_color')``
 | ||
| 
 | ||
|     .. method:: keys()
 | ||
| 
 | ||
|     .. method:: items()
 | ||
| 
 | ||
|     .. method:: setdefault()
 | ||
| 
 | ||
|     .. method:: clear()
 | ||
| 
 | ||
|     It also has these methods:
 | ||
| 
 | ||
|     .. method:: flush()
 | ||
| 
 | ||
|       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:: 1.8
 | ||
| 
 | ||
|           Deletion of the session cookie is a behavior new in Django 1.8.
 | ||
|           Previously, the behavior was to regenerate the session key value that
 | ||
|           was sent back to the user in the cookie.
 | ||
| 
 | ||
|     .. method:: set_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.
 | ||
| 
 | ||
|     .. method:: test_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()`` on a previous, separate page request.
 | ||
|       See `Setting test cookies`_ below for more information.
 | ||
| 
 | ||
|     .. method:: delete_test_cookie()
 | ||
| 
 | ||
|       Deletes the test cookie. Use this to clean up after yourself.
 | ||
| 
 | ||
|     .. method:: set_expiry(value)
 | ||
| 
 | ||
|       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. Note that ``datetime``
 | ||
|         and ``timedelta`` values are only serializable if you are using the
 | ||
|         :class:`~django.contrib.sessions.serializers.PickleSerializer`.
 | ||
| 
 | ||
|       * 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*.
 | ||
| 
 | ||
|     .. method:: get_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`, if there is one, or ``None``.
 | ||
| 
 | ||
|     .. method:: get_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`.
 | ||
| 
 | ||
|     .. method:: get_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.
 | ||
| 
 | ||
|     .. method:: clear_expired()
 | ||
| 
 | ||
|       Removes expired sessions from the session store. This class method is
 | ||
|       called by :djadmin:`clearsessions`.
 | ||
| 
 | ||
|     .. method:: cycle_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.
 | ||
| 
 | ||
| .. _session_serialization:
 | ||
| 
 | ||
| Session serialization
 | ||
| ---------------------
 | ||
| 
 | ||
| Before version 1.6, Django defaulted to using :mod:`pickle` to serialize
 | ||
| session data before storing it in the backend. If you're using the :ref:`signed
 | ||
| cookie session backend<cookie-session-backend>` and :setting:`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 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.
 | ||
| 
 | ||
| This attack can be mitigated by serializing session data using JSON rather
 | ||
| than :mod:`pickle`. To facilitate this, Django 1.5.3 introduced a new setting,
 | ||
| :setting:`SESSION_SERIALIZER`, to customize the session serialization format.
 | ||
| For backwards compatibility, this setting defaults to
 | ||
| using :class:`django.contrib.sessions.serializers.PickleSerializer` in
 | ||
| Django 1.5.x, but, for security hardening, defaults to
 | ||
| :class:`django.contrib.sessions.serializers.JSONSerializer` in Django 1.6.
 | ||
| Even with the caveats described in :ref:`custom-serializers`, we highly
 | ||
| recommend sticking with JSON serialization *especially if you are using the
 | ||
| cookie backend*.
 | ||
| 
 | ||
| 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::
 | ||
| 
 | ||
|         >>> # initial assignment
 | ||
|         >>> request.session[0] = 'bar'
 | ||
|         >>> # subsequent requests following serialization & deserialization
 | ||
|         >>> # of session data
 | ||
|         >>> request.session[0]  # KeyError
 | ||
|         >>> request.session['0']
 | ||
|         'bar'
 | ||
| 
 | ||
|     See the :ref:`custom-serializers` section for more details on limitations
 | ||
|     of JSON serialization.
 | ||
| 
 | ||
| .. class:: serializers.PickleSerializer
 | ||
| 
 | ||
|     Supports arbitrary Python objects, but, as described above, can lead to a
 | ||
|     remote code execution vulnerability if :setting:`SECRET_KEY` becomes known
 | ||
|     by an attacker.
 | ||
| 
 | ||
| .. _custom-serializers:
 | ||
| 
 | ||
| Write Your Own Serializer
 | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
| Note that unlike :class:`~django.contrib.sessions.serializers.PickleSerializer`,
 | ||
| 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 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
 | ||
| ``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.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 an easy way to test whether the user's
 | ||
| browser accepts cookies. Just 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::
 | ||
| 
 | ||
|     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')
 | ||
| 
 | ||
| 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:
 | ||
| 
 | ||
|       >>> 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::
 | ||
| 
 | ||
|     >>> 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.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'
 | ||
| 
 | ||
| If you're using the ``django.contrib.sessions.backends.db`` backend, each
 | ||
| session is just a normal Django model. The ``Session`` model is defined in
 | ||
| ``django/contrib/sessions/models.py``. Because it's a normal model, you can
 | ||
| access sessions using the normal Django database API::
 | ||
| 
 | ||
|     >>> 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}
 | ||
| 
 | ||
| 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 re-opening 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
 | ||
| ==========================
 | ||
| 
 | ||
| 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_SECURE`
 | ||
| * :setting:`SESSION_ENGINE`
 | ||
| * :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE`
 | ||
| * :setting:`SESSION_FILE_PATH`
 | ||
| * :setting:`SESSION_SAVE_EVERY_REQUEST`
 | ||
| 
 | ||
| .. _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 attackers 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` or any
 | ||
|   pickleable Python object when using
 | ||
|   :class:`~django.contrib.sessions.serializers.PickleSerializer`. See the
 | ||
|   :mod:`pickle` module for more information.
 | ||
| 
 | ||
| * 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.
 | ||
| 
 | ||
| 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.
 |