""" Cross Site Request Forgery Middleware. This module provides a middleware that implements protection against request forgeries from other sites. """ import re import itertools try: from functools import wraps except ImportError: from django.utils.functional import wraps # Python 2.3, 2.4 fallback. from django.conf import settings from django.http import HttpResponseForbidden from django.utils.hashcompat import md5_constructor from django.utils.safestring import mark_safe _ERROR_MSG = mark_safe('

403 Forbidden

Cross Site Request Forgery detected. Request aborted.

') _POST_FORM_RE = \ re.compile(r'(]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) _HTML_TYPES = ('text/html', 'application/xhtml+xml') def _make_token(session_id): return md5_constructor(settings.SECRET_KEY + session_id).hexdigest() class CsrfViewMiddleware(object): """ Middleware that requires a present and correct csrfmiddlewaretoken for POST requests that have an active session. """ def process_view(self, request, callback, callback_args, callback_kwargs): if request.method == 'POST': if getattr(callback, 'csrf_exempt', False): return None if request.is_ajax(): return None try: session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] except KeyError: # No session, no check required return None csrf_token = _make_token(session_id) # check incoming token try: request_csrf_token = request.POST['csrfmiddlewaretoken'] except KeyError: return HttpResponseForbidden(_ERROR_MSG) if request_csrf_token != csrf_token: return HttpResponseForbidden(_ERROR_MSG) return None class CsrfResponseMiddleware(object): """ Middleware that post-processes a response to add a csrfmiddlewaretoken if the response/request have an active session. """ def process_response(self, request, response): csrf_token = None try: # This covers a corner case in which the outgoing request # both contains a form and sets a session cookie. This # really should not be needed, since it is best if views # that create a new session (login pages) also do a # redirect, as is done by all such view functions in # Django. cookie = response.cookies[settings.SESSION_COOKIE_NAME] csrf_token = _make_token(cookie.value) except KeyError: # Normal case - look for existing session cookie try: session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] csrf_token = _make_token(session_id) except KeyError: # no incoming or outgoing cookie pass if csrf_token is not None and \ response['Content-Type'].split(';')[0] in _HTML_TYPES: # ensure we don't add the 'id' attribute twice (HTML validity) idattributes = itertools.chain(("id='csrfmiddlewaretoken'",), itertools.repeat('')) def add_csrf_field(match): """Returns the matched
tag plus the added element""" return mark_safe(match.group() + "
" + \ "
") # Modify any POST forms response.content = _POST_FORM_RE.sub(add_csrf_field, response.content) return response class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware): """Django middleware that adds protection against Cross Site Request Forgeries by adding hidden form fields to POST forms and checking requests for the correct value. In the list of middlewares, SessionMiddleware is required, and must come after this middleware. CsrfMiddleWare must come after compression middleware. If a session ID cookie is present, it is hashed with the SECRET_KEY setting to create an authentication token. This token is added to all outgoing POST forms and is expected on all incoming POST requests that have a session ID cookie. If you are setting cookies directly, instead of using Django's session framework, this middleware will not work. CsrfMiddleWare is composed of two middleware, CsrfViewMiddleware and CsrfResponseMiddleware which can be used independently. """ pass def csrf_exempt(view_func): """ Marks a view function as being exempt from the CSRF checks """ def wrapped_view(*args, **kwargs): return view_func(*args, **kwargs) # We could just do view.csrf_exempt = True, but decorators are # nicer if they don't have side-effects. wrapped_view.csrf_exempt = True return wraps(view_func)(wrapped_view)