summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorLuke Plant <L.Plant.98@cantab.net>2009-10-27 00:36:34 +0000
committerLuke Plant <L.Plant.98@cantab.net>2009-10-27 00:36:34 +0000
commit7230a995ce81a7b8dd093bd03cc5ebd34106ee80 (patch)
tree398b0f0b48eb6d4b2c3b69f0e2d7dc2749ec7a74 /django
parent8e70cef9b67433edd70935dcc30c621d1e7fc0a0 (diff)
Moved contrib.csrf.* to core code.
There is stub code for backwards compatiblity with Django 1.1 imports. The documentation has been updated, but has been left in docs/contrib/csrf.txt for now, in order to avoid dead links to documentation on the website. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11661 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/conf/global_settings.py4
-rw-r--r--django/conf/project_template/settings.py2
-rw-r--r--django/contrib/admin/options.py2
-rw-r--r--django/contrib/admin/sites.py3
-rw-r--r--django/contrib/auth/views.py2
-rw-r--r--django/contrib/comments/views/comments.py2
-rw-r--r--django/contrib/comments/views/moderation.py2
-rw-r--r--django/contrib/csrf/context_processors.py20
-rw-r--r--django/contrib/csrf/decorators.py10
-rw-r--r--django/contrib/csrf/middleware.py299
-rw-r--r--django/contrib/csrf/models.py1
-rw-r--r--django/contrib/csrf/tests.py323
-rw-r--r--django/contrib/formtools/wizard.py2
-rw-r--r--django/core/context_processors.py19
-rw-r--r--django/middleware/csrf.py262
-rw-r--r--django/template/context.py2
-rw-r--r--django/views/csrf.py (renamed from django/contrib/csrf/views.py)0
-rw-r--r--django/views/decorators/csrf.py47
18 files changed, 344 insertions, 658 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 62c7dd90c2..a0ce96a818 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -300,7 +300,7 @@ DEFAULT_INDEX_TABLESPACE = ''
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.csrf.middleware.CsrfViewMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.middleware.http.ConditionalGetMiddleware',
# 'django.middleware.gzip.GZipMiddleware',
@@ -381,7 +381,7 @@ PASSWORD_RESET_TIMEOUT_DAYS = 3
# Dotted path to callable to be used as view when a request is
# rejected by the CSRF middleware.
-CSRF_FAILURE_VIEW = 'django.contrib.csrf.views.csrf_failure'
+CSRF_FAILURE_VIEW = 'django.views.csrf.csrf_failure'
# Name and domain for CSRF cookie.
CSRF_COOKIE_NAME = 'csrftoken'
diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py
index f83f3d505a..9b0b516c80 100644
--- a/django/conf/project_template/settings.py
+++ b/django/conf/project_template/settings.py
@@ -60,7 +60,7 @@ TEMPLATE_LOADERS = (
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.csrf.middleware.CsrfViewMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index c702e87340..0119e430b8 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.admin import widgets
from django.contrib.admin import helpers
from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
-from django.contrib.csrf.decorators import csrf_protect
+from django.views.decorators.csrf import csrf_protect
from django.core.exceptions import PermissionDenied
from django.db import models, transaction
from django.db.models.fields import BLANK_CHOICE_DASH
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
index d686540e56..33126999c8 100644
--- a/django/contrib/admin/sites.py
+++ b/django/contrib/admin/sites.py
@@ -3,8 +3,7 @@ from django import http, template
from django.contrib.admin import ModelAdmin
from django.contrib.admin import actions
from django.contrib.auth import authenticate, login
-from django.contrib.csrf.middleware import csrf_response_exempt
-from django.contrib.csrf.decorators import csrf_protect
+from django.views.decorators.csrf import csrf_protect, csrf_response_exempt
from django.db.models.base import ModelBase
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index 9d36710211..d427874df0 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -4,7 +4,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm, PasswordChangeForm
from django.contrib.auth.tokens import default_token_generator
-from django.contrib.csrf.decorators import csrf_protect
+from django.views.decorators.csrf import csrf_protect
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response, get_object_or_404
from django.contrib.sites.models import Site, RequestSite
diff --git a/django/contrib/comments/views/comments.py b/django/contrib/comments/views/comments.py
index ada7e9c77e..7fbe80eead 100644
--- a/django/contrib/comments/views/comments.py
+++ b/django/contrib/comments/views/comments.py
@@ -10,7 +10,7 @@ from django.utils.html import escape
from django.views.decorators.http import require_POST
from django.contrib import comments
from django.contrib.comments import signals
-from django.contrib.csrf.decorators import csrf_protect
+from django.views.decorators.csrf import csrf_protect
class CommentPostBadRequest(http.HttpResponseBadRequest):
"""
diff --git a/django/contrib/comments/views/moderation.py b/django/contrib/comments/views/moderation.py
index 76db326c31..73304ba416 100644
--- a/django/contrib/comments/views/moderation.py
+++ b/django/contrib/comments/views/moderation.py
@@ -5,7 +5,7 @@ from django.contrib.auth.decorators import login_required, permission_required
from utils import next_redirect, confirmation_view
from django.contrib import comments
from django.contrib.comments import signals
-from django.contrib.csrf.decorators import csrf_protect
+from django.views.decorators.csrf import csrf_protect
@csrf_protect
@login_required
diff --git a/django/contrib/csrf/context_processors.py b/django/contrib/csrf/context_processors.py
deleted file mode 100644
index b78030a0b2..0000000000
--- a/django/contrib/csrf/context_processors.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.contrib.csrf.middleware import get_token
-from django.utils.functional import lazy
-
-def csrf(request):
- """
- Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if
- it has not been provided by either a view decorator or the middleware
- """
- def _get_val():
- token = get_token(request)
- if token is None:
- # In order to be able to provide debugging info in the
- # case of misconfiguration, we use a sentinel value
- # instead of returning an empty dict.
- return 'NOTPROVIDED'
- else:
- return token
- _get_val = lazy(_get_val, str)
-
- return {'csrf_token': _get_val() }
diff --git a/django/contrib/csrf/decorators.py b/django/contrib/csrf/decorators.py
deleted file mode 100644
index 67e33bce5c..0000000000
--- a/django/contrib/csrf/decorators.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from django.contrib.csrf.middleware import CsrfViewMiddleware
-from django.utils.decorators import decorator_from_middleware
-
-csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
-csrf_protect.__name__ = "csrf_protect"
-csrf_protect.__doc__ = """
-This decorator adds CSRF protection in exactly the same way as
-CsrfViewMiddleware, but it can be used on a per view basis. Using both, or
-using the decorator multiple times, is harmless and efficient.
-"""
diff --git a/django/contrib/csrf/middleware.py b/django/contrib/csrf/middleware.py
index daee12379e..4885cfcc3e 100644
--- a/django/contrib/csrf/middleware.py
+++ b/django/contrib/csrf/middleware.py
@@ -1,294 +1,7 @@
-"""
-Cross Site Request Forgery Middleware.
+from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware, CsrfResponseMiddleware
+from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, csrf_response_exempt
-This module provides a middleware that implements protection
-against request forgeries from other sites.
-"""
-
-import itertools
-import re
-import random
-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.core.urlresolvers import get_callable
-from django.utils.cache import patch_vary_headers
-from django.utils.hashcompat import md5_constructor
-from django.utils.safestring import mark_safe
-
-_POST_FORM_RE = \
- re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
-
-_HTML_TYPES = ('text/html', 'application/xhtml+xml')
-
-# Use the system (hardware-based) random number generator if it exists.
-if hasattr(random, 'SystemRandom'):
- randrange = random.SystemRandom().randrange
-else:
- randrange = random.randrange
-_MAX_CSRF_KEY = 18446744073709551616L # 2 << 63
-
-def _get_failure_view():
- """
- Returns the view to be used for CSRF rejections
- """
- return get_callable(settings.CSRF_FAILURE_VIEW)
-
-def _get_new_csrf_key():
- return md5_constructor("%s%s"
- % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
-
-def _make_legacy_session_token(session_id):
- return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
-
-def get_token(request):
- """
- Returns the the CSRF token required for a POST form.
-
- A side effect of calling this function is to make the the csrf_protect
- decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
- header to the outgoing response. For this reason, you may need to use this
- function lazily, as is done by the csrf context processor.
- """
- request.META["CSRF_COOKIE_USED"] = True
- return request.META.get("CSRF_COOKIE", None)
-
-class CsrfViewMiddleware(object):
- """
- Middleware that requires a present and correct csrfmiddlewaretoken
- for POST requests that have a CSRF cookie, and sets an outgoing
- CSRF cookie.
-
- This middleware should be used in conjunction with the csrf_token template
- tag.
- """
- def process_view(self, request, callback, callback_args, callback_kwargs):
- if getattr(callback, 'csrf_exempt', False):
- return None
-
- if getattr(request, 'csrf_processing_done', False):
- return None
-
- reject = lambda s: _get_failure_view()(request, reason=s)
- def accept():
- # Avoid checking the request twice by adding a custom attribute to
- # request. This will be relevant when both decorator and middleware
- # are used.
- request.csrf_processing_done = True
- return None
-
- # If the user doesn't have a CSRF cookie, generate one and store it in the
- # request, so it's available to the view. We'll store it in a cookie when
- # we reach the response.
- try:
- request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME]
- cookie_is_new = False
- except KeyError:
- # No cookie, so create one.
- request.META["CSRF_COOKIE"] = _get_new_csrf_key()
- cookie_is_new = True
-
- if request.method == 'POST':
- if getattr(request, '_dont_enforce_csrf_checks', False):
- # Mechanism to turn off CSRF checks for test suite. It comes after
- # the creation of CSRF cookies, so that everything else continues to
- # work exactly the same (e.g. cookies are sent etc), but before the
- # any branches that call reject()
- return accept()
-
- if request.is_ajax():
- # .is_ajax() is based on the presence of X-Requested-With. In
- # the context of a browser, this can only be sent if using
- # XmlHttpRequest. Browsers implement careful policies for
- # XmlHttpRequest:
- #
- # * Normally, only same-domain requests are allowed.
- #
- # * Some browsers (e.g. Firefox 3.5 and later) relax this
- # carefully:
- #
- # * if it is a 'simple' GET or POST request (which can
- # include no custom headers), it is allowed to be cross
- # domain. These requests will not be recognized as AJAX.
- #
- # * if a 'preflight' check with the server confirms that the
- # server is expecting and allows the request, cross domain
- # requests even with custom headers are allowed. These
- # requests will be recognized as AJAX, but can only get
- # through when the developer has specifically opted in to
- # allowing the cross-domain POST request.
- #
- # So in all cases, it is safe to allow these requests through.
- return accept()
-
- if request.is_secure():
- # Strict referer checking for HTTPS
- referer = request.META.get('HTTP_REFERER')
- if referer is None:
- return reject("Referer checking failed - no Referer.")
-
- # The following check ensures that the referer is HTTPS,
- # the domains match and the ports match. This might be too strict.
- good_referer = 'https://%s/' % request.get_host()
- if not referer.startswith(good_referer):
- return reject("Referer checking failed - %s does not match %s." %
- (referer, good_referer))
-
- # If the user didn't already have a CSRF key, then accept the
- # session key for the middleware token, so CSRF protection isn't lost
- # for the period between upgrading to CSRF cookes to the first time
- # each user comes back to the site to receive one.
- if cookie_is_new:
- try:
- session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
- csrf_token = _make_legacy_session_token(session_id)
- except KeyError:
- # No CSRF cookie and no session cookie. For POST requests,
- # we insist on a CSRF cookie, and in this way we can avoid
- # all CSRF attacks, including login CSRF.
- return reject("No CSRF cookie.")
- else:
- csrf_token = request.META["CSRF_COOKIE"]
-
- # check incoming token
- request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
- if request_csrf_token != csrf_token:
- return reject("CSRF token missing or incorrect.")
-
- return accept()
-
- def process_response(self, request, response):
- if getattr(response, 'csrf_processing_done', False):
- return response
-
- # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
- # never called, probaby because a request middleware returned a response
- # (for example, contrib.auth redirecting to a login page).
- if request.META.get("CSRF_COOKIE") is None:
- return response
-
- if not request.META.get("CSRF_COOKIE_USED", False):
- return response
-
- # Set the CSRF cookie even if it's already set, so we renew the expiry timer.
- response.set_cookie(settings.CSRF_COOKIE_NAME,
- request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
- domain=settings.CSRF_COOKIE_DOMAIN)
- # Content varies with the CSRF cookie, so set the Vary header.
- patch_vary_headers(response, ('Cookie',))
- response.csrf_processing_done = True
- return response
-
-class CsrfResponseMiddleware(object):
- """
- DEPRECATED
- Middleware that post-processes a response to add a csrfmiddlewaretoken.
-
- This exists for backwards compatibility and as an interim measure until
- applications are converted to using use the csrf_token template tag
- instead. It will be removed in Django 1.4.
- """
- def __init__(self):
- import warnings
- warnings.warn(
- "CsrfResponseMiddleware and CsrfMiddleware are deprecated; use CsrfViewMiddleware and the template tag instead (see CSRF documentation).",
- PendingDeprecationWarning
- )
-
- def process_response(self, request, response):
- if getattr(response, 'csrf_exempt', False):
- return response
-
- if response['Content-Type'].split(';')[0] in _HTML_TYPES:
- csrf_token = get_token(request)
- # If csrf_token is None, we have no token for this request, which probably
- # means that this is a response from a request middleware.
- if csrf_token is None:
- return response
-
- # 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 <form> tag plus the added <input> element"""
- return mark_safe(match.group() + "<div style='display:none;'>" + \
- "<input type='hidden' " + idattributes.next() + \
- " name='csrfmiddlewaretoken' value='" + csrf_token + \
- "' /></div>")
-
- # Modify any POST forms
- response.content, n = _POST_FORM_RE.subn(add_csrf_field, response.content)
- if n > 0:
- # Content varies with the CSRF cookie, so set the Vary header.
- patch_vary_headers(response, ('Cookie',))
-
- # Since the content has been modified, any Etag will now be
- # incorrect. We could recalculate, but only if we assume that
- # the Etag was set by CommonMiddleware. The safest thing is just
- # to delete. See bug #9163
- del response['ETag']
- return response
-
-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.
-
- CsrfMiddleware uses two middleware, CsrfViewMiddleware and
- CsrfResponseMiddleware, which can be used independently. It is recommended
- to use only CsrfViewMiddleware and use the csrf_token template tag in
- templates for inserting the token.
- """
- # We can't just inherit from CsrfViewMiddleware and CsrfResponseMiddleware
- # because both have process_response methods.
- def __init__(self):
- self.response_middleware = CsrfResponseMiddleware()
- self.view_middleware = CsrfViewMiddleware()
-
- def process_response(self, request, resp):
- # We must do the response post-processing first, because that calls
- # get_token(), which triggers a flag saying that the CSRF cookie needs
- # to be sent (done in CsrfViewMiddleware.process_response)
- resp2 = self.response_middleware.process_response(request, resp)
- return self.view_middleware.process_response(request, resp2)
-
- def process_view(self, request, callback, callback_args, callback_kwargs):
- return self.view_middleware.process_view(request, callback, callback_args,
- callback_kwargs)
-
-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))
+import warnings
+warnings.warn("This import for CSRF functionality is deprecated. Please use django.middleware.csrf for the middleware and django.views.decorators.csrf for decorators.",
+ PendingDeprecationWarning
+ )
diff --git a/django/contrib/csrf/models.py b/django/contrib/csrf/models.py
deleted file mode 100644
index 71abcc5198..0000000000
--- a/django/contrib/csrf/models.py
+++ /dev/null
@@ -1 +0,0 @@
-# models.py file for tests to run.
diff --git a/django/contrib/csrf/tests.py b/django/contrib/csrf/tests.py
deleted file mode 100644
index 14015736c4..0000000000
--- a/django/contrib/csrf/tests.py
+++ /dev/null
@@ -1,323 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from django.test import TestCase
-from django.http import HttpRequest, HttpResponse
-from django.contrib.csrf.middleware import CsrfMiddleware, CsrfViewMiddleware, csrf_exempt
-from django.contrib.csrf.context_processors import csrf
-from django.contrib.sessions.middleware import SessionMiddleware
-from django.utils.importlib import import_module
-from django.conf import settings
-from django.template import RequestContext, Template
-
-# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests
-def post_form_response():
- resp = HttpResponse(content="""
-<html><body><form method="POST"><input type="text" /></form></body></html>
-""", mimetype="text/html")
- return resp
-
-def post_form_response_non_html():
- resp = post_form_response()
- resp["Content-Type"] = "application/xml"
- return resp
-
-def post_form_view(request):
- """A view that returns a POST form (without a token)"""
- return post_form_response()
-
-# Response/views used for template tag tests
-def _token_template():
- return Template("{% csrf_token %}")
-
-def _render_csrf_token_template(req):
- context = RequestContext(req, processors=[csrf])
- template = _token_template()
- return template.render(context)
-
-def token_view(request):
- """A view that uses {% csrf_token %}"""
- return HttpResponse(_render_csrf_token_template(request))
-
-def non_token_view_using_request_processor(request):
- """
- A view that doesn't use the token, but does use the csrf view processor.
- """
- context = RequestContext(request, processors=[csrf])
- template = Template("")
- return HttpResponse(template.render(context))
-
-class TestingHttpRequest(HttpRequest):
- """
- A version of HttpRequest that allows us to change some things
- more easily
- """
- def is_secure(self):
- return getattr(self, '_is_secure', False)
-
-class CsrfMiddlewareTest(TestCase):
- _csrf_id = "1"
-
- # This is a valid session token for this ID and secret key. This was generated using
- # the old code that we're to be backwards-compatible with. Don't use the CSRF code
- # to generate this hash, or we're merely testing the code against itself and not
- # checking backwards-compatibility. This is also the output of (echo -n test1 | md5sum).
- _session_token = "5a105e8b9d40e1329780d62ea2265d8a"
- _session_id = "1"
- _secret_key_for_session_test= "test"
-
- def _get_GET_no_csrf_cookie_request(self):
- return TestingHttpRequest()
-
- def _get_GET_csrf_cookie_request(self):
- req = TestingHttpRequest()
- req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id
- return req
-
- def _get_POST_csrf_cookie_request(self):
- req = self._get_GET_csrf_cookie_request()
- req.method = "POST"
- return req
-
- def _get_POST_no_csrf_cookie_request(self):
- req = self._get_GET_no_csrf_cookie_request()
- req.method = "POST"
- return req
-
- def _get_POST_request_with_token(self):
- req = self._get_POST_csrf_cookie_request()
- req.POST['csrfmiddlewaretoken'] = self._csrf_id
- return req
-
- def _get_POST_session_request_with_token(self):
- req = self._get_POST_no_csrf_cookie_request()
- req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
- req.POST['csrfmiddlewaretoken'] = self._session_token
- return req
-
- def _get_POST_session_request_no_token(self):
- req = self._get_POST_no_csrf_cookie_request()
- req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
- return req
-
- def _check_token_present(self, response, csrf_id=None):
- self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
-
- # Check the post processing and outgoing cookie
- def test_process_response_no_csrf_cookie(self):
- """
- When no prior CSRF cookie exists, check that the cookie is created and a
- token is inserted.
- """
- req = self._get_GET_no_csrf_cookie_request()
- CsrfMiddleware().process_view(req, post_form_view, (), {})
-
- resp = post_form_response()
- resp_content = resp.content # needed because process_response modifies resp
- resp2 = CsrfMiddleware().process_response(req, resp)
-
- csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
- self.assertNotEqual(csrf_cookie, False)
- self.assertNotEqual(resp_content, resp2.content)
- self._check_token_present(resp2, csrf_cookie.value)
- # Check the Vary header got patched correctly
- self.assert_('Cookie' in resp2.get('Vary',''))
-
- def test_process_response_no_csrf_cookie_view_only_get_token_used(self):
- """
- When no prior CSRF cookie exists, check that the cookie is created, even
- if only CsrfViewMiddleware is used.
- """
- # This is checking that CsrfViewMiddleware has the cookie setting
- # code. Most of the other tests use CsrfMiddleware.
- req = self._get_GET_no_csrf_cookie_request()
- # token_view calls get_token() indirectly
- CsrfViewMiddleware().process_view(req, token_view, (), {})
- resp = token_view(req)
- resp2 = CsrfViewMiddleware().process_response(req, resp)
-
- csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
- self.assertNotEqual(csrf_cookie, False)
-
- def test_process_response_get_token_not_used(self):
- """
- Check that if get_token() is not called, the view middleware does not
- add a cookie.
- """
- # This is important to make pages cacheable. Pages which do call
- # get_token(), assuming they use the token, are not cacheable because
- # the token is specific to the user
- req = self._get_GET_no_csrf_cookie_request()
- # non_token_view_using_request_processor does not call get_token(), but
- # does use the csrf request processor. By using this, we are testing
- # that the view processor is properly lazy and doesn't call get_token()
- # until needed.
- CsrfViewMiddleware().process_view(req, non_token_view_using_request_processor, (), {})
- resp = non_token_view_using_request_processor(req)
- resp2 = CsrfViewMiddleware().process_response(req, resp)
-
- csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
- self.assertEqual(csrf_cookie, False)
-
- def test_process_response_existing_csrf_cookie(self):
- """
- Check that the token is inserted when a prior CSRF cookie exists
- """
- req = self._get_GET_csrf_cookie_request()
- CsrfMiddleware().process_view(req, post_form_view, (), {})
-
- resp = post_form_response()
- resp_content = resp.content # needed because process_response modifies resp
- resp2 = CsrfMiddleware().process_response(req, resp)
- self.assertNotEqual(resp_content, resp2.content)
- self._check_token_present(resp2)
-
- def test_process_response_non_html(self):
- """
- Check the the post-processor does nothing for content-types not in _HTML_TYPES.
- """
- req = self._get_GET_no_csrf_cookie_request()
- CsrfMiddleware().process_view(req, post_form_view, (), {})
- resp = post_form_response_non_html()
- resp_content = resp.content # needed because process_response modifies resp
- resp2 = CsrfMiddleware().process_response(req, resp)
- self.assertEquals(resp_content, resp2.content)
-
- def test_process_response_exempt_view(self):
- """
- Check that no post processing is done for an exempt view
- """
- req = self._get_POST_csrf_cookie_request()
- resp = csrf_exempt(post_form_view)(req)
- resp_content = resp.content
- resp2 = CsrfMiddleware().process_response(req, resp)
- self.assertEquals(resp_content, resp2.content)
-
- # Check the request processing
- def test_process_request_no_session_no_csrf_cookie(self):
- """
- Check that if neither a CSRF cookie nor a session cookie are present,
- the middleware rejects the incoming request. This will stop login CSRF.
- """
- req = self._get_POST_no_csrf_cookie_request()
- req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(403, req2.status_code)
-
- def test_process_request_csrf_cookie_no_token(self):
- """
- Check that if a CSRF cookie is present but no token, the middleware
- rejects the incoming request.
- """
- req = self._get_POST_csrf_cookie_request()
- req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(403, req2.status_code)
-
- def test_process_request_csrf_cookie_and_token(self):
- """
- Check that if both a cookie and a token is present, the middleware lets it through.
- """
- req = self._get_POST_request_with_token()
- req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(None, req2)
-
- def test_process_request_session_cookie_no_csrf_cookie_token(self):
- """
- When no CSRF cookie exists, but the user has a session, check that a token
- using the session cookie as a legacy CSRF cookie is accepted.
- """
- orig_secret_key = settings.SECRET_KEY
- settings.SECRET_KEY = self._secret_key_for_session_test
- try:
- req = self._get_POST_session_request_with_token()
- req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(None, req2)
- finally:
- settings.SECRET_KEY = orig_secret_key
-
- def test_process_request_session_cookie_no_csrf_cookie_no_token(self):
- """
- Check that if a session cookie is present but no token and no CSRF cookie,
- the request is rejected.
- """
- req = self._get_POST_session_request_no_token()
- req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(403, req2.status_code)
-
- def test_process_request_csrf_cookie_no_token_exempt_view(self):
- """
- Check that if a CSRF cookie is present and no token, but the csrf_exempt
- decorator has been applied to the view, the middleware lets it through
- """
- req = self._get_POST_csrf_cookie_request()
- req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
- self.assertEquals(None, req2)
-
- def test_ajax_exemption(self):
- """
- Check that AJAX requests are automatically exempted.
- """
- req = self._get_POST_csrf_cookie_request()
- req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
- req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(None, req2)
-
- # Tests for the template tag method
- def test_token_node_no_csrf_cookie(self):
- """
- Check that CsrfTokenNode works when no CSRF cookie is set
- """
- req = self._get_GET_no_csrf_cookie_request()
- resp = token_view(req)
- self.assertEquals(u"", resp.content)
-
- def test_token_node_with_csrf_cookie(self):
- """
- Check that CsrfTokenNode works when a CSRF cookie is set
- """
- req = self._get_GET_csrf_cookie_request()
- CsrfViewMiddleware().process_view(req, token_view, (), {})
- resp = token_view(req)
- self._check_token_present(resp)
-
- def test_token_node_with_new_csrf_cookie(self):
- """
- Check that CsrfTokenNode works when a CSRF cookie is created by
- the middleware (when one was not already present)
- """
- req = self._get_GET_no_csrf_cookie_request()
- CsrfViewMiddleware().process_view(req, token_view, (), {})
- resp = token_view(req)
- resp2 = CsrfViewMiddleware().process_response(req, resp)
- csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME]
- self._check_token_present(resp, csrf_id=csrf_cookie.value)
-
- def test_response_middleware_without_view_middleware(self):
- """
- Check that CsrfResponseMiddleware finishes without error if the view middleware
- has not been called, as is the case if a request middleware returns a response.
- """
- req = self._get_GET_no_csrf_cookie_request()
- resp = post_form_view(req)
- CsrfMiddleware().process_response(req, resp)
-
- def test_https_bad_referer(self):
- """
- Test that a POST HTTPS request with a bad referer is rejected
- """
- req = self._get_POST_request_with_token()
- req._is_secure = True
- req.META['HTTP_HOST'] = 'www.example.com'
- req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage'
- req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
- self.assertNotEqual(None, req2)
- self.assertEquals(403, req2.status_code)
-
- def test_https_good_referer(self):
- """
- Test that a POST HTTPS request with a good referer is accepted
- """
- req = self._get_POST_request_with_token()
- req._is_secure = True
- req.META['HTTP_HOST'] = 'www.example.com'
- req.META['HTTP_REFERER'] = 'https://www.example.com/somepage'
- req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
- self.assertEquals(None, req2)
diff --git a/django/contrib/formtools/wizard.py b/django/contrib/formtools/wizard.py
index 60fe314217..4729e155b8 100644
--- a/django/contrib/formtools/wizard.py
+++ b/django/contrib/formtools/wizard.py
@@ -14,7 +14,7 @@ from django.template.context import RequestContext
from django.utils.hashcompat import md5_constructor
from django.utils.translation import ugettext_lazy as _
from django.contrib.formtools.utils import security_hash
-from django.contrib.csrf.decorators import csrf_protect
+from django.views.decorators.csrf import csrf_protect
class FormWizard(object):
# Dictionary of extra template context variables.
diff --git a/django/core/context_processors.py b/django/core/context_processors.py
index 0dbd8449b6..b950dba0f6 100644
--- a/django/core/context_processors.py
+++ b/django/core/context_processors.py
@@ -8,6 +8,7 @@ RequestContext.
"""
from django.conf import settings
+from django.middleware.csrf import get_token
from django.utils.functional import lazy, memoize, SimpleLazyObject
def auth(request):
@@ -40,6 +41,24 @@ def auth(request):
'perms': lazy(lambda: PermWrapper(get_user()), PermWrapper)(),
}
+def csrf(request):
+ """
+ Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if
+ it has not been provided by either a view decorator or the middleware
+ """
+ def _get_val():
+ token = get_token(request)
+ if token is None:
+ # In order to be able to provide debugging info in the
+ # case of misconfiguration, we use a sentinel value
+ # instead of returning an empty dict.
+ return 'NOTPROVIDED'
+ else:
+ return token
+ _get_val = lazy(_get_val, str)
+
+ return {'csrf_token': _get_val() }
+
def debug(request):
"Returns context variables helpful for debugging."
context_extras = {}
diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py
new file mode 100644
index 0000000000..339487bbcc
--- /dev/null
+++ b/django/middleware/csrf.py
@@ -0,0 +1,262 @@
+"""
+Cross Site Request Forgery Middleware.
+
+This module provides a middleware that implements protection
+against request forgeries from other sites.
+"""
+
+import itertools
+import re
+import random
+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.core.urlresolvers import get_callable
+from django.utils.cache import patch_vary_headers
+from django.utils.hashcompat import md5_constructor
+from django.utils.safestring import mark_safe
+
+_POST_FORM_RE = \
+ re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
+
+_HTML_TYPES = ('text/html', 'application/xhtml+xml')
+
+# Use the system (hardware-based) random number generator if it exists.
+if hasattr(random, 'SystemRandom'):
+ randrange = random.SystemRandom().randrange
+else:
+ randrange = random.randrange
+_MAX_CSRF_KEY = 18446744073709551616L # 2 << 63
+
+def _get_failure_view():
+ """
+ Returns the view to be used for CSRF rejections
+ """
+ return get_callable(settings.CSRF_FAILURE_VIEW)
+
+def _get_new_csrf_key():
+ return md5_constructor("%s%s"
+ % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
+
+def _make_legacy_session_token(session_id):
+ return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
+
+def get_token(request):
+ """
+ Returns the the CSRF token required for a POST form.
+
+ A side effect of calling this function is to make the the csrf_protect
+ decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
+ header to the outgoing response. For this reason, you may need to use this
+ function lazily, as is done by the csrf context processor.
+ """
+ request.META["CSRF_COOKIE_USED"] = True
+ return request.META.get("CSRF_COOKIE", None)
+
+class CsrfViewMiddleware(object):
+ """
+ Middleware that requires a present and correct csrfmiddlewaretoken
+ for POST requests that have a CSRF cookie, and sets an outgoing
+ CSRF cookie.
+
+ This middleware should be used in conjunction with the csrf_token template
+ tag.
+ """
+ def process_view(self, request, callback, callback_args, callback_kwargs):
+ if getattr(callback, 'csrf_exempt', False):
+ return None
+
+ if getattr(request, 'csrf_processing_done', False):
+ return None
+
+ reject = lambda s: _get_failure_view()(request, reason=s)
+ def accept():
+ # Avoid checking the request twice by adding a custom attribute to
+ # request. This will be relevant when both decorator and middleware
+ # are used.
+ request.csrf_processing_done = True
+ return None
+
+ # If the user doesn't have a CSRF cookie, generate one and store it in the
+ # request, so it's available to the view. We'll store it in a cookie when
+ # we reach the response.
+ try:
+ request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME]
+ cookie_is_new = False
+ except KeyError:
+ # No cookie, so create one.
+ request.META["CSRF_COOKIE"] = _get_new_csrf_key()
+ cookie_is_new = True
+
+ if request.method == 'POST':
+ if getattr(request, '_dont_enforce_csrf_checks', False):
+ # Mechanism to turn off CSRF checks for test suite. It comes after
+ # the creation of CSRF cookies, so that everything else continues to
+ # work exactly the same (e.g. cookies are sent etc), but before the
+ # any branches that call reject()
+ return accept()
+
+ if request.is_ajax():
+ # .is_ajax() is based on the presence of X-Requested-With. In
+ # the context of a browser, this can only be sent if using
+ # XmlHttpRequest. Browsers implement careful policies for
+ # XmlHttpRequest:
+ #
+ # * Normally, only same-domain requests are allowed.
+ #
+ # * Some browsers (e.g. Firefox 3.5 and later) relax this
+ # carefully:
+ #
+ # * if it is a 'simple' GET or POST request (which can
+ # include no custom headers), it is allowed to be cross
+ # domain. These requests will not be recognized as AJAX.
+ #
+ # * if a 'preflight' check with the server confirms that the
+ # server is expecting and allows the request, cross domain
+ # requests even with custom headers are allowed. These
+ # requests will be recognized as AJAX, but can only get
+ # through when the developer has specifically opted in to
+ # allowing the cross-domain POST request.
+ #
+ # So in all cases, it is safe to allow these requests through.
+ return accept()
+
+ if request.is_secure():
+ # Strict referer checking for HTTPS
+ referer = request.META.get('HTTP_REFERER')
+ if referer is None:
+ return reject("Referer checking failed - no Referer.")
+
+ # The following check ensures that the referer is HTTPS,
+ # the domains match and the ports match. This might be too strict.
+ good_referer = 'https://%s/' % request.get_host()
+ if not referer.startswith(good_referer):
+ return reject("Referer checking failed - %s does not match %s." %
+ (referer, good_referer))
+
+ # If the user didn't already have a CSRF key, then accept the
+ # session key for the middleware token, so CSRF protection isn't lost
+ # for the period between upgrading to CSRF cookes to the first time
+ # each user comes back to the site to receive one.
+ if cookie_is_new:
+ try:
+ session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
+ csrf_token = _make_legacy_session_token(session_id)
+ except KeyError:
+ # No CSRF cookie and no session cookie. For POST requests,
+ # we insist on a CSRF cookie, and in this way we can avoid
+ # all CSRF attacks, including login CSRF.
+ return reject("No CSRF cookie.")
+ else:
+ csrf_token = request.META["CSRF_COOKIE"]
+
+ # check incoming token
+ request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
+ if request_csrf_token != csrf_token:
+ return reject("CSRF token missing or incorrect.")
+
+ return accept()
+
+ def process_response(self, request, response):
+ if getattr(response, 'csrf_processing_done', False):
+ return response
+
+ # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
+ # never called, probaby because a request middleware returned a response
+ # (for example, contrib.auth redirecting to a login page).
+ if request.META.get("CSRF_COOKIE") is None:
+ return response
+
+ if not request.META.get("CSRF_COOKIE_USED", False):
+ return response
+
+ # Set the CSRF cookie even if it's already set, so we renew the expiry timer.
+ response.set_cookie(settings.CSRF_COOKIE_NAME,
+ request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
+ domain=settings.CSRF_COOKIE_DOMAIN)
+ # Content varies with the CSRF cookie, so set the Vary header.
+ patch_vary_headers(response, ('Cookie',))
+ response.csrf_processing_done = True
+ return response
+
+class CsrfResponseMiddleware(object):
+ """
+ DEPRECATED
+ Middleware that post-processes a response to add a csrfmiddlewaretoken.
+
+ This exists for backwards compatibility and as an interim measure until
+ applications are converted to using use the csrf_token template tag
+ instead. It will be removed in Django 1.4.
+ """
+ def __init__(self):
+ import warnings
+ warnings.warn(
+ "CsrfResponseMiddleware and CsrfMiddleware are deprecated; use CsrfViewMiddleware and the template tag instead (see CSRF documentation).",
+ PendingDeprecationWarning
+ )
+
+ def process_response(self, request, response):
+ if getattr(response, 'csrf_exempt', False):
+ return response
+
+ if response['Content-Type'].split(';')[0] in _HTML_TYPES:
+ csrf_token = get_token(request)
+ # If csrf_token is None, we have no token for this request, which probably
+ # means that this is a response from a request middleware.
+ if csrf_token is None:
+ return response
+
+ # 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 <form> tag plus the added <input> element"""
+ return mark_safe(match.group() + "<div style='display:none;'>" + \
+ "<input type='hidden' " + idattributes.next() + \
+ " name='csrfmiddlewaretoken' value='" + csrf_token + \
+ "' /></div>")
+
+ # Modify any POST forms
+ response.content, n = _POST_FORM_RE.subn(add_csrf_field, response.content)
+ if n > 0:
+ # Content varies with the CSRF cookie, so set the Vary header.
+ patch_vary_headers(response, ('Cookie',))
+
+ # Since the content has been modified, any Etag will now be
+ # incorrect. We could recalculate, but only if we assume that
+ # the Etag was set by CommonMiddleware. The safest thing is just
+ # to delete. See bug #9163
+ del response['ETag']
+ return response
+
+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.
+
+ CsrfMiddleware uses two middleware, CsrfViewMiddleware and
+ CsrfResponseMiddleware, which can be used independently. It is recommended
+ to use only CsrfViewMiddleware and use the csrf_token template tag in
+ templates for inserting the token.
+ """
+ # We can't just inherit from CsrfViewMiddleware and CsrfResponseMiddleware
+ # because both have process_response methods.
+ def __init__(self):
+ self.response_middleware = CsrfResponseMiddleware()
+ self.view_middleware = CsrfViewMiddleware()
+
+ def process_response(self, request, resp):
+ # We must do the response post-processing first, because that calls
+ # get_token(), which triggers a flag saying that the CSRF cookie needs
+ # to be sent (done in CsrfViewMiddleware.process_response)
+ resp2 = self.response_middleware.process_response(request, resp)
+ return self.view_middleware.process_response(request, resp2)
+
+ def process_view(self, request, callback, callback_args, callback_kwargs):
+ return self.view_middleware.process_view(request, callback, callback_args,
+ callback_kwargs)
+
diff --git a/django/template/context.py b/django/template/context.py
index 5fbdaf3a0d..f57a3aaa64 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -6,7 +6,7 @@ _standard_context_processors = None
# We need the CSRF processor no matter what the user has in their settings,
# because otherwise it is a security vulnerability, and we can't afford to leave
# this to human error or failure to read migration instructions.
-_builtin_context_processors = ('django.contrib.csrf.context_processors.csrf',)
+_builtin_context_processors = ('django.core.context_processors.csrf',)
class ContextPopException(Exception):
"pop() has been called more times than push()"
diff --git a/django/contrib/csrf/views.py b/django/views/csrf.py
index dd8a8966b1..dd8a8966b1 100644
--- a/django/contrib/csrf/views.py
+++ b/django/views/csrf.py
diff --git a/django/views/decorators/csrf.py b/django/views/decorators/csrf.py
new file mode 100644
index 0000000000..b789872efe
--- /dev/null
+++ b/django/views/decorators/csrf.py
@@ -0,0 +1,47 @@
+from django.middleware.csrf import CsrfViewMiddleware
+from django.utils.decorators import decorator_from_middleware
+try:
+ from functools import wraps
+except ImportError:
+ from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
+
+csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
+csrf_protect.__name__ = "csrf_protect"
+csrf_protect.__doc__ = """
+This decorator adds CSRF protection in exactly the same way as
+CsrfViewMiddleware, but it can be used on a per view basis. Using both, or
+using the decorator multiple times, is harmless and efficient.
+"""
+
+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))