diff --git a/django/contrib/csrf/__init__.py b/django/contrib/csrf/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/django/contrib/csrf/middleware.py b/django/contrib/csrf/middleware.py new file mode 100644 index 0000000000000..4c00461b898cd --- /dev/null +++ b/django/contrib/csrf/middleware.py @@ -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 = "

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.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 = "
" + \ + "
" + response.content = _POST_FORM_RE.sub('\\1' + extra_field, response.content) + return response diff --git a/docs/add_ons.txt b/docs/add_ons.txt index e602e429ad5cf..28df4f55b6ebd 100644 --- a/docs/add_ons.txt +++ b/docs/add_ons.txt @@ -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 ============= diff --git a/docs/csrf.txt b/docs/csrf.txt new file mode 100644 index 0000000000000..4ea09552fc6bf --- /dev/null +++ b/docs/csrf.txt @@ -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. +