diff -r 000000000000 -r 0d40e90630ef web/lib/django/contrib/csrf/middleware.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/contrib/csrf/middleware.py Wed Jan 20 00:34:04 2010 +0100 @@ -0,0 +1,160 @@ +""" +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\s*=\s*(\'|"|)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): + if getattr(response, 'csrf_exempt', False): + return response + + csrf_token = None + try: + # This covers a corner case in which the outgoing response + # 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_response_exempt(view_func): + """ + Modifies a view function so that its response is exempt + from the post-processing of the CSRF middleware. + """ + def wrapped_view(*args, **kwargs): + resp = view_func(*args, **kwargs) + resp.csrf_exempt = True + return resp + return wraps(view_func)(wrapped_view) + +def csrf_view_exempt(view_func): + """ + Marks a view function as being exempt from CSRF view protection. + """ + # We could just do view_func.csrf_exempt = True, but decorators + # are nicer if they don't have side-effects, so we return a new + # function. + def wrapped_view(*args, **kwargs): + return view_func(*args, **kwargs) + wrapped_view.csrf_exempt = True + return wraps(view_func)(wrapped_view) + +def csrf_exempt(view_func): + """ + Marks a view function as being exempt from the CSRF checks + and post processing. + + This is the same as using both the csrf_view_exempt and + csrf_response_exempt decorators. + """ + return csrf_response_exempt(csrf_view_exempt(view_func))