Secure Client Side Sessions

Since a few changesets Werkzeug provides a module for client side sessions. While it’s of course not yet perfect I think it’s an interesting approach and we’re currently replacing django’s sessions with Werkzeug’s secure cookie.

How does it work? Basically the data is stored as pickled data inside the cookie. The pickled data is hashed and added to the cookie too. Whenever the data is loaded from the cookie a new hash is created and the two hashes are compared. If they match the data is unserialized, otherwise a new SecureCookie object is created with empty data. As a matter of fact there is no “session key” so if you want to use it with django you have to fake the “session key” by inserting it into the data. We use the following session middleware as replacement for the django session middleware:

try:
    from hashlib import md5
except ImportError:
    from md5 import md5
from time import time
from random import random
from django.conf import settings
from django.utils.http import cookie_date
from werkzeug.contrib.securecookie import SecureCookie

class Session(SecureCookie):

    @property
    def session_key(self):
        if not 'session_key' in self:
            self['session_key'] = md5('%s%s%s' % (random(), time(),
                                      settings.SECRET_KEY)).hexdigest()
        return self['session_key']

class ClientSideSessionMiddleware(object):

    def process_request(self, request):
        data = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        if data:
            session = Session.unserialize(data, settings.SECRET_KEY)
        else:
            session = Session(secret_key=settings.SECRET_KEY)
        request.session = session

    def process_response(self, request, response):
        try:
            modified = request.session.modified
        except AttributeError:
            return response

        if modified or settings.SESSION_SAVE_EVERY_REQUEST:
            if request.session.get('is_permanent_session'):
                max_age = settings.SESSION_COOKIE_AGE
                expires_time = time() + settings.SESSION_COOKIE_AGE
                expires = cookie_date(expires_time)
            else:
                max_age = expires = None
            response.set_cookie(settings.SESSION_COOKIE_NAME,
                                request.session.serialize(),
                                max_age=max_age, expires=expires,
                                domain=settings.SESSION_COOKIE_DOMAIN,
                                secure=settings.SESSION_COOKIE_SECURE or None)
        return response

Additionally to the normal django session middleware this middleware can handle both persistent sessions and browser-session bound sessions. Per default a session ends at the end of the browser session, if you want to make it persistent you have to set “is_permanent_session” in the session data to True.

Keep in mind that cookies have a size limit and that users will be able to look at that data (but not alter it). The example above requires the current werkzeug tip and not the 0.1 release version.

2 Responses to “Secure Client Side Sessions”

  1. Couldn’t you have just written a custom session backend to do this?

    Comment by SmileyChris — Sunday, December 16th, 2007 @ 4:41 am
  2. You mean a django session backend? Primarly because it wasn’t written for django. That you can use it with django is just a goody that comes for free because the module doesn’t require any specific Werkzeug implementation details.

    And even if someone wants to integrate it into the django session system in the first place there would be no other way than writing a different session middleware because the session store cannot alter the cookie that is set.

    Comment by Armin Ronacher — Sunday, December 16th, 2007 @ 10:50 am

Leave a Reply

cogitations driven by wordpress