Added CsrfMiddleware to contrib, and documentation.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@2868 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Luke Plant 2006-05-08 23:03:08 +00:00
parent f0141f1c1c
commit 8eecb95ec8
4 changed files with 162 additions and 0 deletions

View File

View File

@ -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

View File

@ -57,6 +57,16 @@ See the `syndication documentation`_.
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/
csrf
====
A middleware for preventing Cross Site Request Forgeries
See the `csrf documentation`_.
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
Other add-ons
=============

68
docs/csrf.txt Normal file
View File

@ -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.