Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django signed cookie session storage, replay attacks, and SESSION_COOKIE_AGE

As per the Django documentation, signed cookie session storage is vulnerable to a replay attack:

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

Does this mean that:

  1. We are relying on the client-side expiration of cookies to ensure that the session data is destroyed (thus a replay attack is still possible if the cookie contents are captured before the cookie is removed by the browser), or
  2. The server detects the staleness of the data (comparing with SESSION_COOKIE_AGE and explicitly rejects data it deems as stale.

It seems technically possible for Django to be able to determine how 'old' a session is without persisting this data server-side, but the docs don't seem clear about whether or not this is being done, or is Django relying on / trusting the user's browser to kill old cookies (and thus the session could still be replayed if the data was captured prior to its expiry).

like image 754
Kye Russell Avatar asked Feb 22 '19 02:02

Kye Russell


People also ask

What is signed cookie in Django?

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.

How does Django store data in session?

Using database-backed sessions If you want to use a database-backed session, you need to add 'django. contrib. sessions' to your INSTALLED_APPS setting. Once you have configured your installation, run manage.py migrate to install the single database table that stores session data.

What is session and cookies in Django?

Django uses a cookie containing a special session id to identify each browser and its associated session with the site. The actual session data is stored in the site database by default (this is more secure than storing the data in a cookie, where they are more vulnerable to malicious users).


1 Answers

Django, through SessionMiddleware, does set the HTTP cookie expiration to SESSION_COOKIE_AGE. This tells the browser when the cookie should be considered "too old" and expired. This is good housekeeping. Also, if the browser detects that a cookie should not be used anymore, it won't use it and this saves Django from spending time examining a cookie can't be used anyway.

However, Django does not rely on the browsers to ensure that expired cookies cannot ever be used again. A rogue agent could get a copy of the cookie and then reuse it past its expiration date. Django prevents this by sending the cookie to the browser formatted like this:

<payload>:<creation stamp>:<signature>

The colons are separators appearing literally in the data. <payload> is the actual session data. <creation stamp> is a time stamp added when the cookie is created, and <signature> is the MAC signature that signs the payload and the creation stamp.

You can see the relevant code in django.core.signing. The comments at the top of the file give you the basic signing scenarios using the Signer class. This class does not know about timestamps. However, when signing cookies, Django uses TimestampSigner, which does know about timestamps and produces the format I've shown above.

When TimestampSigner unsigns a cookie, it checks the timestamp and raises an exception if it is too old:

def unsign(self, value, max_age=None):
    """
    Retrieve original value and check it wasn't signed more
    than max_age seconds ago.
    """
    result = super().unsign(value)
    value, timestamp = result.rsplit(self.sep, 1)
    timestamp = baseconv.base62.decode(timestamp)
    if max_age is not None:
        if isinstance(max_age, datetime.timedelta):
            max_age = max_age.total_seconds()
        # Check timestamp is not older than max_age
        age = time.time() - timestamp
        if age > max_age:
            raise SignatureExpired(
                'Signature age %s > %s seconds' % (age, max_age))
    return value
like image 68
Louis Avatar answered Sep 27 '22 21:09

Louis