A form with built-in CSRF protection. Include CsrfCookieMiddleware in your MIDDLEWARE_SETTINGS, subclass SafeForm and off you go. See: this django-developers post for more info.
[edit] This form is actually WAY overengineered currently. Will update soon.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # forms.py
from django import forms
from django.conf import settings
import hmac
import hashlib
import base64
import time
from utils import random_chars
from csrf_settings import *
class SafeForm(forms.Form):
# how long is the form valid (in seconds)
FORM_TIMEOUT = 1800
csrf_field = forms.CharField(widget=forms.HiddenInput)
def __init__(self, request, *args, **kwargs):
# must have CsrfCookieMiddleware enabled
assert CSRF_COOKIE_NAME in request.COOKIES
super(SafeForm, self).__init__(*args, **kwargs)
if not self.is_bound:
# create an initial value for the field
tstamp = str(int(time.time()))
salt = random_chars(SALT_LENGTH)
cookie = request.COOKIES[CSRF_COOKIE_NAME]
sig = tstamp + salt + self.__class__.__name__ + cookie
sig = hmac.new(settings.SECRET_KEY, sig, digestmod=hashlib.sha1).hexdigest()
final = base64.b64encode('/'.join([tstamp, salt, sig]))
self.fields['csrf_field'].initial = final
else:
self._csrf_cookie = request.COOKIES[CSRF_COOKIE_NAME]
def clean_csrf_field(self):
tstamp, salt, sig = base64.b64decode(self.cleaned_data['csrf_field']).split('/')
new_sig = tstamp + salt + self.__class__.__name__ + self._csrf_cookie
new_sig = hmac.new(settings.SECRET_KEY, new_sig, digestmod=hashlib.sha1).hexdigest()
if sig != new_sig:
raise forms.ValidationError, 'You are under attack!'
if int(time.time()) - int(tstamp) > SafeForm.FORM_TIMEOUT:
raise forms.ValidationError, 'Form session has expired'
# middleware.py
from csrf_settings import *
from utils import random_chars
class CsrfCookieMiddleware(object):
def process_response(self, request, response):
if CSRF_COOKIE_NAME not in request.COOKIES:
response.set_cookie(CSRF_COOKIE_NAME, random_chars(CSRF_COOKIE_LENGTH))
return response
# utils.py
import string
import random
def random_chars(length):
alphanum = string.ascii_letters + string.digits
return ''.join(random.choice(alphanum) for i in range(length))
# csrf_settings.py
CSRF_COOKIE_LENGTH = 40
CSRF_COOKIE_NAME = 'no_csrf'
SALT_LENGTH = 4
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Using middleware to set the CSRF cookie is a smart approach. I'm not sure if I like it though - I want CSRF protection to be ridiculously easy to turn on (just use SafeForm instead of regular Form) and most Django projects I work on end up with far too many middleware classes as it is. I'd rather avoid telling people to apply Yet Another middleware.
#
I agree with Willison.
This middleware is going to add the CSRF cookie to every response.
As for random_chars:
#
Please login first before commenting.