Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added CsrfMiddleware to contrib, and documentation.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@2868 bcc190cf-cafb-0310-a4f2-bffc1f526a37
- Loading branch information
1 parent
f0141f1
commit 8eecb95
Showing
4 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
""" | ||
Cross Site Request Forgery Middleware. | ||
This module provides a middleware that implements protection | ||
against request forgeries from other sites. | ||
""" | ||
from django.conf import settings | ||
from django.http import HttpResponseForbidden | ||
import md5 | ||
import re | ||
|
||
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>" | ||
|
||
_POST_FORM_RE = \ | ||
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) | ||
|
||
_HTML_TYPES = ('text/html', 'application/xhtml+xml') | ||
|
||
def _make_token(session_id): | ||
return md5.new(settings.SECRET_KEY + session_id).hexdigest() | ||
|
||
class CsrfMiddleware(object): | ||
"""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. | ||
""" | ||
|
||
def process_request(self, request): | ||
if request.POST: | ||
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 | ||
|
||
def process_response(self, request, response): | ||
csrf_token = None | ||
try: | ||
cookie = response.cookies[settings.SESSION_COOKIE_NAME] | ||
csrf_token = _make_token(cookie.value) | ||
except KeyError: | ||
# No outgoing cookie to set session, but | ||
# a session might already exist. | ||
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: | ||
|
||
# Modify any POST forms | ||
extra_field = "<div style='display:none;'>" + \ | ||
"<input type='hidden' name='csrfmiddlewaretoken' value='" + \ | ||
csrf_token + "' /></div>" | ||
response.content = _POST_FORM_RE.sub('\\1' + extra_field, response.content) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
===================================== | ||
Cross Site Request Forgery Protection | ||
===================================== | ||
|
||
The CsrfMiddleware class provides easy-to-use protection against | ||
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious | ||
web site creates a link or form button that is intended to perform some action | ||
on your web site, using the credentials of a logged-in user who is tricked | ||
into clicking on the link in their browser. | ||
|
||
The first defense against CSRF attacks is to ensure that GET requests | ||
are side-effect free. POST requests can then be protected by adding this | ||
middleware into your list of installed middleware. | ||
|
||
|
||
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF | ||
|
||
How to use it | ||
============= | ||
Add the middleware ``"django.contrib.csrf.middleware.CsrfMiddleware"`` to | ||
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process | ||
the response after the SessionMiddleware, so must come before it in the | ||
list. It also must process the response before things like compression | ||
happen to the response, so it must come after GZipMiddleware in the list. | ||
|
||
How it works | ||
============ | ||
CsrfMiddleware does two things: | ||
|
||
1. It modifies outgoing requests by adding a hidden form field to all | ||
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is | ||
a hash of the session ID plus a secret. If there is no session ID set, | ||
this modification of the response isn't done, so there is very little | ||
performance penalty for those requests that don't have a session. | ||
|
||
2. On all incoming POST requests that have the session cookie set, it | ||
checks that the 'csrfmiddlewaretoken' is present and correct. If it | ||
isn't, the user will get a 403 error. | ||
|
||
This ensures that only forms that have originated from your web site | ||
can be used to POST data back. | ||
|
||
It deliberately only targets HTTP POST requests (and the corresponding | ||
POST forms). GET requests ought never to have side effects (if you are | ||
using HTTP GET and POST correctly), and so a CSRF attack with a GET | ||
request will always be harmless. | ||
|
||
POST requests that are not accompanied by a session cookie are not protected, | ||
but they do not need to be protected, since the 'attacking' web site | ||
could make these kind of requests anyway. | ||
|
||
The Content-Type is checked before modifying the response, and only | ||
pages that are served as 'text/html' or 'application/xml+xhtml' | ||
are modified. | ||
|
||
Limitations | ||
=========== | ||
CsrfMiddleware requires Django's session framework to work. If you have | ||
a custom authentication system that manually sets cookies and the like, | ||
it won't help you. | ||
|
||
If your app creates HTML pages and forms in some unusual way, (e.g. | ||
it sends fragments of HTML in javascript document.write statements) | ||
you might bypass the filter that adds the hidden field to the form, | ||
in which case form submission will always fail. It may still be possible | ||
to use the middleware, provided you can find some way to get the | ||
CSRF token and ensure that is included when your form is submitted. | ||
|