On Mon, Sep 6, 2010 at 9:11 PM, Patryk Zawadzki <pat...@pld-linux.org> wrote:
> Consider the following example. It was based on the code I wrote to
> handle the new Facebook API so it might be a bit of an overkill but it
> shows it's possible. A simpler solution would be to return (TTL + ':'
> + md5(token + user_ip + secret_key + TTL)).
>
> def encode_cookie(token):
>    values = {
>        'TTL': time.time() + 60*60,
>        'token': token,
>    }
>    payload = simplejson.dumps(values)
>    digest = hmac.new(settings.SECRET_KEY, payload, hashlib.sha256).digest()
>    encoded_payload = base64.b64encode(payload)
>    encoded_digest = base64.b64encode(digest)
>    return '%s.%s' % (encoded_digest, encoded_payload)
>
> def decode_cookie(value):
>    encoded_sig, encoded_payload = map(str, value.split('.', 1))
>    sig = base64.b64decode(encoded_sig)
>    payload = base64.b64decode(encoded_payload)
>    data = simplejson.loads(payload)
>    digest = hmac.new(settings.SECRET_KEY, payload, hashlib.sha256).digest()
>    if str(digest) != sig:
>        return None
>    if data.get('TTL', 0) < time.time():
>        return None
>    return data.get('token', None)

Another approach would be not to use a cookie at all. For each {%
csrf_token %} use a slightly modified variant of the above
encode_cookie function with:

values = {
    'host: request.META['HTTP_HOST'],
    'scheme': request.is_secure(),
    'user_ip': request.META['REMOTE_ADDR'],
    'user_agent': request.META['HTTP_USER_AGENT'],
    'ttl': time.time() + 30*60,
}

Then when handling a POST request, decipher the token and compare each
META field with the ones from the request and validate ttl against
time.time(). I believe it's not less secure than the current
implementation and solves two problems:

1) each form served gets its own ttl, an attacker can't keep pinging
the server to keep the token alive
2) each token serves for a single use and will inevitably timeout in
30 minutes while still allowing you to open two forms in two browser
tabs and submit each of them separately

Attaching such an encoded string to the request should be harmless, it
won't be much longer than the cookie header, will not be sent with
each request (only sent when rendering a form, only received with form
submission).

If you like this approach, I suggest letting the form both generate
and validate the token if the middleware is not active. See my other
CSRF thread for the use case.

-- 
Patryk Zawadzki

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-develop...@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to