1
# Copyright 2010 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
5
Taken verbatim from Django 1.1.1.
7
Cross Site Request Forgery Middleware.
9
This module provides a middleware that implements protection
10
against request forgeries from other sites.
16
from functools import wraps
18
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
20
from django.conf import settings
21
from django.http import HttpResponseForbidden
22
from django.utils.hashcompat import md5_constructor
23
from django.utils.safestring import mark_safe
24
from django.utils.translation import ugettext as _
26
_ERROR_MSG = mark_safe(_('<html xmlns="http://www.w3.org/1999/xhtml" '
27
'xml:lang="en"><body><h1>403 Forbidden</h1><p>'
28
'Cross Site Request Forgery detected. Request '
29
'aborted.</p></body></html>'))
32
re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)',
35
_HTML_TYPES = ('text/html', 'application/xhtml+xml')
38
def _make_token(session_id):
39
return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
42
class CsrfViewMiddleware(object):
44
Middleware that requires a present and correct csrfmiddlewaretoken
45
for POST requests that have an active session.
47
def process_view(self, request, callback, callback_args, callback_kwargs):
48
if request.method == 'POST':
49
if getattr(callback, 'csrf_exempt', False):
56
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
58
# No session, no check required
61
csrf_token = _make_token(session_id)
62
# check incoming token
64
request_csrf_token = request.POST['csrfmiddlewaretoken']
66
return HttpResponseForbidden(_ERROR_MSG)
68
if request_csrf_token != csrf_token:
69
return HttpResponseForbidden(_ERROR_MSG)
74
class CsrfResponseMiddleware(object):
76
Middleware that post-processes a response to add a
77
csrfmiddlewaretoken if the response/request have an active
80
def process_response(self, request, response):
81
if getattr(response, 'csrf_exempt', False):
86
# This covers a corner case in which the outgoing response
87
# both contains a form and sets a session cookie. This
88
# really should not be needed, since it is best if views
89
# that create a new session (login pages) also do a
90
# redirect, as is done by all such view functions in
92
cookie = response.cookies[settings.SESSION_COOKIE_NAME]
93
csrf_token = _make_token(cookie.value)
95
# Normal case - look for existing session cookie
97
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
98
csrf_token = _make_token(session_id)
100
# no incoming or outgoing cookie
103
if csrf_token is not None and \
104
response['Content-Type'].split(';')[0] in _HTML_TYPES:
106
# ensure we don't add the 'id' attribute twice (HTML validity)
107
idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
108
itertools.repeat(''))
110
def add_csrf_field(match):
111
"""Returns the matched <form> tag plus the added <input>
115
match.group() + "<div style='display:none;'>" + \
116
"<input type='hidden' " + idattributes.next() + \
117
" name='csrfmiddlewaretoken' value='" + csrf_token + \
120
# Modify any POST forms
121
response.content = _POST_FORM_RE.sub(add_csrf_field,
126
class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware):
127
"""Django middleware that adds protection against Cross Site
128
Request Forgeries by adding hidden form fields to POST forms and
129
checking requests for the correct value.
131
In the list of middlewares, SessionMiddleware is required, and
132
must come after this middleware. CsrfMiddleWare must come after
133
compression middleware.
135
If a session ID cookie is present, it is hashed with the
136
SECRET_KEY setting to create an authentication token. This token
137
is added to all outgoing POST forms and is expected on all
138
incoming POST requests that have a session ID cookie.
140
If you are setting cookies directly, instead of using Django's
141
session framework, this middleware will not work.
143
CsrfMiddleWare is composed of two middleware, CsrfViewMiddleware
144
and CsrfResponseMiddleware which can be used independently.
149
def csrf_response_exempt(view_func):
151
Modifies a view function so that its response is exempt
152
from the post-processing of the CSRF middleware.
154
def wrapped_view(*args, **kwargs):
155
resp = view_func(*args, **kwargs)
156
resp.csrf_exempt = True
158
return wraps(view_func)(wrapped_view)
161
def csrf_view_exempt(view_func):
163
Marks a view function as being exempt from CSRF view protection.
165
# We could just do view_func.csrf_exempt = True, but decorators
166
# are nicer if they don't have side-effects, so we return a new
168
def wrapped_view(*args, **kwargs):
169
return view_func(*args, **kwargs)
170
wrapped_view.csrf_exempt = True
171
return wraps(view_func)(wrapped_view)
174
def csrf_exempt(view_func):
176
Marks a view function as being exempt from the CSRF checks
179
This is the same as using both the csrf_view_exempt and
180
csrf_response_exempt decorators.
182
return csrf_response_exempt(csrf_view_exempt(view_func))