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
This commit is contained in:
Luke Plant 2009-10-27 00:36:34 +00:00
parent 8e70cef9b6
commit 7230a995ce
26 changed files with 394 additions and 355 deletions

View File

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

View File

@ -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',
)

View File

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

View File

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

View File

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

View File

@ -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):
"""

View File

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

View File

@ -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() }

View File

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

View File

@ -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).",
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
)
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))

View File

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

View File

@ -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 = {}

262
django/middleware/csrf.py Normal file
View File

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

View File

@ -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()"

View File

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

View File

@ -19,6 +19,9 @@ their deprecation, as per the :ref:`Django deprecation policy
token. ``CsrfMiddleware``, which combines ``CsrfResponseMiddleware``
and ``CsrfViewMiddleware``, is also deprecated.
* The old imports for CSRF functionality (``django.contrib.csrf.*``),
which moved to core in 1.2, will be removed.
* 2.0
* ``django.views.defaults.shortcut()``. This function has been moved
to ``django.contrib.contenttypes.views.shortcut()`` as part of the

View File

@ -4,7 +4,7 @@
Cross Site Request Forgery protection
=====================================
.. module:: django.contrib.csrf
.. module:: django.middleware.csrf
:synopsis: Protects against Cross Site Request Forgeries
The CSRF middleware and template tag provides easy-to-use protection against
@ -37,13 +37,13 @@ How to use it
To enable CSRF protection for your views, follow these steps:
1. Add the middleware
``'django.contrib.csrf.middleware.CsrfViewMiddleware'`` to your list of
``'django.middleware.csrf.CsrfViewMiddleware'`` to your list of
middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come
before ``CsrfResponseMiddleware`` if that is being used, and before any
view middleware that assume that CSRF attacks have been dealt with.)
Alternatively, you can use the decorator
``django.contrib.csrf.decorators.csrf_protect`` on particular views you
``django.views.decorators.csrf.csrf_protect`` on particular views you
want to protect. This is **not recommended** by itself, since if you
forget to use it, you will have a security hole. The 'belt and braces'
strategy of using both is fine, and will incur minimal overhead.
@ -57,11 +57,11 @@ To enable CSRF protection for your views, follow these steps:
that would cause the CSRF token to be leaked, leading to a vulnerability.
3. In the corresponding view functions, ensure that the
``'django.contrib.csrf.context_processors.csrf'`` context processor is
``'django.core.context_processors.csrf'`` context processor is
being used. Usually, this can be done in one of two ways:
1. Use RequestContext, which always uses
``'django.contrib.csrf.context_processors.csrf'`` (no matter what your
``'django.core.context_processors.csrf'`` (no matter what your
TEMPLATE_CONTEXT_PROCESSORS setting). If you are using
generic views or contrib apps, you are covered already, since these
apps use RequestContext throughout.
@ -69,7 +69,7 @@ To enable CSRF protection for your views, follow these steps:
2. Manually import and use the processor to generate the CSRF token and
add it to the template context. e.g.::
from django.contrib.csrf.context_processors import csrf
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def my_view(request):
@ -96,7 +96,7 @@ as ``CsrfResponseMiddleware``, and it can be used by following these steps:
1. Follow step 1 above to install ``CsrfViewMiddleware``.
2. Add ``'django.contrib.csrf.middleware.CsrfResponseMiddleware'`` to your
2. Add ``'django.middleware.csrf.CsrfResponseMiddleware'`` to your
:setting:`MIDDLEWARE_CLASSES` setting.
``CsrfResponseMiddleware`` needs to process the response before things
@ -140,6 +140,28 @@ enabled any CSRF protection. This section outlines the steps necessary for a
smooth upgrade, without having to fix all the applications to use the new
template tag method immediately.
First of all, the location of the middleware and related functions have
changed. There are backwards compatible stub files so that old imports will
continue to work for now, but they are deprecated and will be removed in Django
1.4. The following changes have been made:
* Middleware have been moved to ``django.middleware.csrf``
* Decorators have been moved to ``django.views.decorators.csrf``
====================================================== ==============================================
Old New
====================================================== ==============================================
django.contrib.csrf.middleware.CsrfMiddleware django.middleware.csrf.CsrfMiddleware
django.contrib.csrf.middleware.CsrfViewMiddleware django.middleware.csrf.CsrfViewMiddleware
django.contrib.csrf.middleware.CsrfResponseMiddleware django.middleware.csrf.CsrfResponseMiddleware
django.contrib.csrf.middleware.csrf_exempt django.views.decorators.csrf_exempt
django.contrib.csrf.middleware.csrf_view_exempt django.views.decorators.csrf_view_exempt
django.contrib.csrf.middleware.csrf_response_exempt django.views.decorators.csrf_response_exempt
====================================================== ==============================================
You should update any imports, and also the paths in your
:setting:`MIDDLEWARE_CLASSES`.
If you have ``CsrfMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`, you will now
have a working installation with CSRF protection. It is recommended at this
point that you replace ``CsrfMiddleware`` with its two components,
@ -186,9 +208,9 @@ Exceptions
To manually exclude a view function from being handled by either of the two CSRF
middleware, you can use the ``csrf_exempt`` decorator, found in the
``django.contrib.csrf.middleware`` module. For example::
``django.views.decorators.csrf`` module. For example::
from django.contrib.csrf.middleware import csrf_exempt
from django.views.decorators.csrf import csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
@ -246,7 +268,7 @@ The CSRF protection is based on the following things:
This cookie is set by ``CsrfViewMiddleware``. It is meant to be permanent,
but since there is no way to set a cookie that never expires, it is sent with
every response that has called ``django.contrib.csrf.middleware.get_token()``
every response that has called ``django.middleware.csrf.get_token()``
(the function used internally to retrieve the CSRF token).
2. A hidden form field with the name 'csrfmiddlewaretoken' present in all
@ -352,7 +374,7 @@ If you are using ``CsrfResponseMiddleware`` and 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. You should
use the template tag or :meth:`django.contrib.csrf.middleware.get_token` to get
use the template tag or :meth:`django.middleware.csrf.get_token` to get
the CSRF token and ensure it is included when your form is submitted.
Contrib and reusable apps

View File

@ -165,11 +165,11 @@ every incoming ``HttpRequest`` object. See :ref:`Authentication in Web requests
CSRF protection middleware
--------------------------
.. module:: django.contrib.csrf.middleware
.. module:: django.middleware.csrf
:synopsis: Middleware adding protection against Cross Site Request
Forgeries.
.. class:: django.contrib.csrf.middleware.CsrfMiddleware
.. class:: django.middleware.csrf.CsrfMiddleware
.. versionadded:: 1.0

View File

@ -171,7 +171,7 @@ accepted by accepted by a view served from another subdomain.
CSRF_FAILURE_VIEW
-----------------
Default: ``'django.contrib.csrf.views.csrf_failure'``
Default: ``'django.views.csrf.csrf_failure'``
A dotted path to the view function to be used when an incoming request
is rejected by the CSRF protection. The function should have this signature::
@ -789,7 +789,7 @@ Default::
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.csrf.middleware.CsrfViewMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',)
A tuple of middleware classes to use. See :ref:`topics-http-middleware`.

View File

@ -315,7 +315,7 @@ and return a dictionary of items to be merged into the context. By default,
.. versionadded:: 1.2
In addition to these, ``RequestContext`` always uses
``'django.contrib.csrf.context_processors.csrf'``. This is a security
``'django.core.context_processors.csrf'``. This is a security
related context processor required by the admin and other contrib apps, and,
in case of accidental misconfiguration, it is deliberately hardcoded in and
cannot be turned off by the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
@ -411,8 +411,8 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every
``RequestContext`` will contain a variable ``MEDIA_URL``, providing the
value of the :setting:`MEDIA_URL` setting.
django.contrib.csrf.context_processors.csrf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
django.core.context_processors.csrf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.2

View File

@ -18,6 +18,9 @@ changes that developers must be aware of:
POST requests need to be written to work with the middleware. Instructions
on how to do this are found in the CSRF docs.
* All of the CSRF has moved from contrib to core (with backwards compatible
imports in the old locations, which are deprecated).
LazyObject
----------

View File

@ -29,7 +29,7 @@ created by :djadmin:`django-admin.py startproject <startproject>`::
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',
)

View File

@ -2,8 +2,9 @@
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.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware
from django.views.decorators.csrf import csrf_exempt
from django.core.context_processors import csrf
from django.contrib.sessions.middleware import SessionMiddleware
from django.utils.importlib import import_module
from django.conf import settings