Using Sessions in Views

When SessionMiddleware is activated, each 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 also edit it multiple times.

All session objects inherit from the base class backends.base.SessionBase. It has the following standard dictionary methods:

  • __getitem__(key)
  • __setitem__(key, value)
  • __delitem__(key)
  • __contains__(key)
  • get(key, default=None)
  • pop(key)
  • keys()
  • items()
  • setdefault()
  • clear()

It also has these methods:

flush()

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

set_test_cookie()

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.

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.

delete_test_cookie()

Deletes the test cookie. Use this to clean up after yourself.

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 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.

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 SESSION_COOKIE_AGE. This function accepts two optional keyword arguments:

  • modification: last modification of the session, as a datetime object. Defaults to the current time.
  • expiry: expiry information for the session, as a datetime object, an int (in seconds), or None. Defaults to the value stored in the session by set_expiry(), if there is one, or None.

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 SESSION_COOKIE_AGE seconds from now. This function accepts the same keyword arguments as get_expiry_age().

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.

clear_expired()

Removes expired sessions from the session store. This class method is called by clearsessions.

cycle_key()

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

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.

Session Serialization

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 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 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 from Django 1.6 onward.

Even with the caveats described in custom-serializers, we highly recommend sticking with JSON serialization especially if you are using the cookie backend.

Bundled Serializers

serializers.JSONSerializer

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 custom-serializers section for more details on limitations of JSON serialization.

serializers.PickleSerializer

Supports arbitrary Python objects, but, as described above, can lead to a remote code execution vulnerability if SECRET_KEY becomes known by an attacker.

Write Your Own Serializer

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 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).

Your serializer class must implement two methods, dumps(self, obj) and loads(self, data), to serialize and deserialize the dictionary of session data, respectively.

Setting Test Cookies

As a convenience, Django provides an easy way to test whether the user’s browser accepts cookies. Just call the set_test_cookie() method of request.session in a view, and call 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 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')