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:
parent
8e70cef9b6
commit
7230a995ce
|
@ -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'
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() }
|
|
@ -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.
|
||||
"""
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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()"
|
||||
|
|
|
@ -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))
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
----------
|
||||
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue