Signed Cookies

15 views
Skip to first unread message

Gulopine

unread,
Jan 10, 2007, 9:43:30 PM1/10/07
to Django users
In response to some discussion on Chapter 20 of the Django book, I'd
like to submit my first contrib package for consideration, a middleware
package that transparently implements signed cookies. It uses the
SECRET_KEY setting already included in Django, and could easily be
modified to allow different encryption algorithms (currently it simply
uses md5). The only configuration is the addition of the package to
MIDDLEWARE_CLASSES, everything else is handled automatically.

A note on using this with other middlewares. Its position in the
middleware class list defines which other middlewares receive the
security of signed cookies, but there's slightly more to it than that.
In order to ensure that the values passed for further processing are in
fact valid, any cookies not containing a valid signature will be
removed from the HttpRequest instance. For the most part, this is fine,
as long as this middleware is placed before any other middlewares that
use cookies. The only potential advantage to placing a cookie-using
middleware after this one is a savings of 33 characters per cookie,
which can sometimes help, given the length restriction on cookie
values.

However, in the case of SessionMiddleware (and perhaps others), the
value stored is known to just be an ID, and not any actual usable
values. For this case, it's safe to place SessionMiddleware before this
one, and it will work as usual, with the advantage that the 'sessionid'
cookie will not incur the additional 33 characters taken up by the
signing process. However, this also also means that the 'sessionid'
cookie will not be available to any middlewares listed after this one,
nor will it be available in the view. SessionMiddleware stores the
session_key it its own internal structure, so this shouldn't actually
pose any real-world problems. It's just a caveat that should be known
in case it's used with other middlewares where it might cause problems.

The code for middleware.py follows (I've placed it in
django.contrib.signed_cookies, but any other name would suffice):

import re
try:
from hashlib import md5 as hash
except ImportError:
from md5 import new as hash

from django.conf import settings

regex = re.compile(r'([0-9a-f]+):(.*)$')

class SignedCookiesMiddleware(object):

def process_request(self, request):
for (key, signed_value) in request.COOKIES.items():
try:
(signature, value) = regex.match(signed_value).groups()
assert signature == self.get_digest(key, value)
request.COOKIES[key] = value
except:
del request.COOKIES[key]

def process_response(self, request, response):
for (key, morsel) in response.cookies.items():
if morsel['expires'] == 0 and morsel['max-age'] == 0:
continue
digest = self.get_digest(key, morsel.value)
response.set_cookie(key, '%s:%s' % (digest, morsel.value),
max_age=morsel['max-age'],
expires=morsel['expires'],
path=morsel['path'],
domain=morsel['domain'],
secure=morsel['secure']
)
return response

def get_digest(self, key, value):
string = ':'.join([settings.SECRET_KEY, key, value])
return hash(string).hexdigest()

Jeremy Dunck

unread,
Jan 11, 2007, 9:58:26 AM1/11/07
to django...@googlegroups.com
On 1/10/07, Gulopine <gulo...@gmail.com> wrote:
...

> The code for middleware.py follows (I've placed it in
> django.contrib.signed_cookies, but any other name would suffice):
>

This looks useful, but please re-send the code as an attachment to
avoid email line wrap munging. :)

Jacob Kaplan-Moss

unread,
Jan 11, 2007, 10:34:58 AM1/11/07
to django...@googlegroups.com

Better would be to open a ticket at code.djangoproject.com and then take this
up on django-developers.

[But this looks pretty damn good, I've got to say...]

Jacob

Gulopine

unread,
Jan 11, 2007, 2:53:19 PM1/11/07
to Django users
> Better would be to open a ticket at code.djangoproject.com and then take this
> up on django-developers.

Done (ticket #3285), and done. Is that the normal procedure, then, for
contrib apps? The note on the bottom of the contrib add-on page says to
take it up here, which is what I did. However, the "Contributing to
Django" page seems to recommend the same course you did. A little more
consistency there might help people contribute.

Mainly though, I just want to make sure I have the procedure right,
because I have a few other things going for a site I'm working on that
will probably end up as contrib proposals. Considerably larger than a
basic middleware like this. I just want to get it right.

>
> [But this looks pretty damn good, I've got to say...]
>
> Jacob

Hey, thanks! I have to say, I've taken to Python/Django extremely well
for somebody who worked solely in PHP (and had never seen a line of
Python code!) less than a year ago. :)

-Gulopine

Reply all
Reply to author
Forward
0 new messages