Removed deprecated CsrfResponseMiddleware, and corresponding tests and docs
git-svn-id: http://code.djangoproject.com/svn/django/trunk@15949 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
21ef64e34c
commit
8823021625
|
@ -6,7 +6,6 @@ against request forgeries from other sites.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import itertools
|
|
||||||
import re
|
import re
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
@ -15,14 +14,8 @@ from django.core.urlresolvers import get_callable
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.utils.http import same_origin
|
from django.utils.http import same_origin
|
||||||
from django.utils.log import getLogger
|
from django.utils.log import getLogger
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils.crypto import constant_time_compare
|
from django.utils.crypto import constant_time_compare
|
||||||
|
|
||||||
_POST_FORM_RE = \
|
|
||||||
re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
|
|
||||||
|
|
||||||
_HTML_TYPES = ('text/html', 'application/xhtml+xml')
|
|
||||||
|
|
||||||
logger = getLogger('django.request')
|
logger = getLogger('django.request')
|
||||||
|
|
||||||
# Use the system (hardware-based) random number generator if it exists.
|
# Use the system (hardware-based) random number generator if it exists.
|
||||||
|
@ -49,10 +42,6 @@ def _get_new_csrf_key():
|
||||||
return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
|
return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def _make_legacy_session_token(session_id):
|
|
||||||
return hashlib.md5(settings.SECRET_KEY + session_id).hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
def get_token(request):
|
def get_token(request):
|
||||||
"""
|
"""
|
||||||
Returns the the CSRF token required for a POST form. The token is an
|
Returns the the CSRF token required for a POST form. The token is an
|
||||||
|
@ -214,83 +203,3 @@ class CsrfViewMiddleware(object):
|
||||||
patch_vary_headers(response, ('Cookie',))
|
patch_vary_headers(response, ('Cookie',))
|
||||||
response.csrf_processing_done = True
|
response.csrf_processing_done = True
|
||||||
return response
|
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).",
|
|
||||||
DeprecationWarning
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
|
@ -17,28 +17,18 @@ The first defense against CSRF attacks is to ensure that GET requests are
|
||||||
side-effect free. POST requests can then be protected by following the steps
|
side-effect free. POST requests can then be protected by following the steps
|
||||||
below.
|
below.
|
||||||
|
|
||||||
.. versionadded:: 1.2
|
|
||||||
The 'contrib' apps, including the admin, use the functionality described
|
|
||||||
here. Because it is security related, a few things have been added to core
|
|
||||||
functionality to allow this to happen without any required upgrade steps.
|
|
||||||
|
|
||||||
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
||||||
|
|
||||||
How to use it
|
How to use it
|
||||||
=============
|
=============
|
||||||
|
|
||||||
.. versionchanged:: 1.2
|
|
||||||
The template tag functionality (the recommended way to use this) was added
|
|
||||||
in version 1.2. The previous method (still available) is described under
|
|
||||||
`Legacy method`_.
|
|
||||||
|
|
||||||
To enable CSRF protection for your views, follow these steps:
|
To enable CSRF protection for your views, follow these steps:
|
||||||
|
|
||||||
1. Add the middleware
|
1. Add the middleware
|
||||||
``'django.middleware.csrf.CsrfViewMiddleware'`` to your list of
|
``'django.middleware.csrf.CsrfViewMiddleware'`` to your list of
|
||||||
middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come
|
middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come
|
||||||
before ``CsrfResponseMiddleware`` if that is being used, and before any
|
and before any view middleware that assume that CSRF attacks have
|
||||||
view middleware that assume that CSRF attacks have been dealt with.)
|
been dealt with.)
|
||||||
|
|
||||||
Alternatively, you can use the decorator
|
Alternatively, you can use the decorator
|
||||||
``django.views.decorators.csrf.csrf_protect`` on particular views you
|
``django.views.decorators.csrf.csrf_protect`` on particular views you
|
||||||
|
@ -47,7 +37,7 @@ To enable CSRF protection for your views, follow these steps:
|
||||||
2. In any template that uses a POST form, use the :ttag:`csrf_token` tag inside
|
2. In any template that uses a POST form, use the :ttag:`csrf_token` tag inside
|
||||||
the ``<form>`` element if the form is for an internal URL, e.g.::
|
the ``<form>`` element if the form is for an internal URL, e.g.::
|
||||||
|
|
||||||
<form action="" method="post">{% csrf_token %}
|
<form action="." method="post">{% csrf_token %}
|
||||||
|
|
||||||
This should not be done for POST forms that target external URLs, since
|
This should not be done for POST forms that target external URLs, since
|
||||||
that would cause the CSRF token to be leaked, leading to a vulnerability.
|
that would cause the CSRF token to be leaked, leading to a vulnerability.
|
||||||
|
@ -78,8 +68,8 @@ To enable CSRF protection for your views, follow these steps:
|
||||||
takes care of this step for you.
|
takes care of this step for you.
|
||||||
|
|
||||||
The utility script ``extras/csrf_migration_helper.py`` can help to automate the
|
The utility script ``extras/csrf_migration_helper.py`` can help to automate the
|
||||||
finding of code and templates that may need to be upgraded. It contains full
|
finding of code and templates that may need these steps. It contains full help
|
||||||
help on how to use it.
|
on how to use it.
|
||||||
|
|
||||||
.. _csrf-ajax:
|
.. _csrf-ajax:
|
||||||
|
|
||||||
|
@ -146,145 +136,9 @@ Use of the decorator 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
|
use it, you will have a security hole. The 'belt and braces' strategy of using
|
||||||
both is fine, and will incur minimal overhead.
|
both is fine, and will incur minimal overhead.
|
||||||
|
|
||||||
Legacy method
|
|
||||||
-------------
|
|
||||||
|
|
||||||
In Django 1.1, the template tag did not exist. Instead, a post-processing
|
|
||||||
middleware that re-wrote POST forms to include the CSRF token was used. If you
|
|
||||||
are upgrading a site from version 1.1 or earlier, please read this section and
|
|
||||||
the `Upgrading notes`_ below. The post-processing middleware is still available
|
|
||||||
as ``CsrfResponseMiddleware``, and it can be used by following these steps:
|
|
||||||
|
|
||||||
1. Follow step 1 above to install ``CsrfViewMiddleware``.
|
|
||||||
|
|
||||||
2. Add ``'django.middleware.csrf.CsrfResponseMiddleware'`` to your
|
|
||||||
:setting:`MIDDLEWARE_CLASSES` setting.
|
|
||||||
|
|
||||||
``CsrfResponseMiddleware`` needs to process the response before things
|
|
||||||
like compression or setting ofETags happen to the response, so it must
|
|
||||||
come after ``GZipMiddleware``, ``CommonMiddleware`` and
|
|
||||||
``ConditionalGetMiddleware`` in the list. It also must come after
|
|
||||||
``CsrfViewMiddleware``.
|
|
||||||
|
|
||||||
Use of the ``CsrfResponseMiddleware`` is not recommended because of the
|
|
||||||
performance hit it imposes, and because of a potential security problem (see
|
|
||||||
below). It can be used as an interim measure until applications have been
|
|
||||||
updated to use the :ttag:`csrf_token` tag. It is deprecated and will be
|
|
||||||
removed in Django 1.4.
|
|
||||||
|
|
||||||
Django 1.1 and earlier provided a single ``CsrfMiddleware`` class. This is also
|
|
||||||
still available for backwards compatibility. It combines the functions of the
|
|
||||||
two middleware.
|
|
||||||
|
|
||||||
Note also that previous versions of these classes depended on the sessions
|
|
||||||
framework, but this dependency has now been removed, with backward compatibility
|
|
||||||
support so that upgrading will not produce any issues.
|
|
||||||
|
|
||||||
Security of legacy method
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The post-processing ``CsrfResponseMiddleware`` adds the CSRF token to all POST
|
|
||||||
forms (unless the view has been decorated with ``csrf_response_exempt``). If
|
|
||||||
the POST form has an external untrusted site as its target, rather than an
|
|
||||||
internal page, that site will be sent the CSRF token when the form is submitted.
|
|
||||||
Armed with this leaked information, that site will then be able to successfully
|
|
||||||
launch a CSRF attack on your site against that user. The
|
|
||||||
``@csrf_response_exempt`` decorator can be used to fix this, but only if the
|
|
||||||
page doesn't also contain internal forms that require the token.
|
|
||||||
|
|
||||||
.. _ref-csrf-upgrading-notes:
|
|
||||||
|
|
||||||
Upgrading notes
|
|
||||||
---------------
|
|
||||||
|
|
||||||
When upgrading to version 1.2 or later, you may have applications that rely on
|
|
||||||
the old post-processing functionality for CSRF protection, or you may not have
|
|
||||||
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.csrf_exempt
|
|
||||||
django.contrib.csrf.middleware.csrf_view_exempt django.views.decorators.csrf.csrf_view_exempt
|
|
||||||
django.contrib.csrf.middleware.csrf_response_exempt django.views.decorators.csrf.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,
|
|
||||||
``CsrfViewMiddleware`` and ``CsrfResponseMiddleware`` (in that order).
|
|
||||||
|
|
||||||
If you do not have any of the middleware in your :setting:`MIDDLEWARE_CLASSES`,
|
|
||||||
you will have a working installation but without any CSRF protection for your
|
|
||||||
views (just as you had before). It is strongly recommended to install
|
|
||||||
``CsrfViewMiddleware`` and ``CsrfResponseMiddleware``, as described above.
|
|
||||||
|
|
||||||
Note that contrib apps, such as the admin, have been updated to use the
|
|
||||||
``csrf_protect`` decorator, so that they are secured even if you do not add the
|
|
||||||
``CsrfViewMiddleware`` to your settings. However, if you have supplied
|
|
||||||
customised templates to any of the view functions of contrib apps (whether
|
|
||||||
explicitly via a keyword argument, or by overriding built-in templates), **you
|
|
||||||
MUST update them** to include the :ttag:`csrf_token` template tag as described
|
|
||||||
above, or they will stop working. (If you cannot update these templates for
|
|
||||||
some reason, you will be forced to use ``CsrfResponseMiddleware`` for these
|
|
||||||
views to continue working).
|
|
||||||
|
|
||||||
Note also, if you are using the comments app, and you are not going to add
|
|
||||||
``CsrfViewMiddleware`` to your settings (not recommended), you will need to add
|
|
||||||
the ``csrf_protect`` decorator to any views that include the comment forms and
|
|
||||||
target the comment views (usually using the :ttag:`comment_form_target` template
|
|
||||||
tag).
|
|
||||||
|
|
||||||
Assuming you have followed the above, all views in your Django site will now be
|
|
||||||
protected by the ``CsrfViewMiddleware``. Contrib apps meet the requirements
|
|
||||||
imposed by the ``CsrfViewMiddleware`` using the template tag, and other
|
|
||||||
applications in your project will meet its requirements by virtue of the
|
|
||||||
``CsrfResponseMiddleware``.
|
|
||||||
|
|
||||||
The next step is to update all your applications to use the template tag, as
|
|
||||||
described in `How to use it`_, steps 2-3. This can be done as soon as is
|
|
||||||
practical. Any applications that are updated will now require Django 1.1.2 or
|
|
||||||
later, since they will use the CSRF template tag which was not available in
|
|
||||||
earlier versions. (The template tag in 1.1.2 is actually a no-op that exists
|
|
||||||
solely to ease the transition to 1.2 — it allows apps to be created that have
|
|
||||||
CSRF protection under 1.2 without requiring users of the apps to upgrade to the
|
|
||||||
Django 1.2.X series).
|
|
||||||
|
|
||||||
The utility script ``extras/csrf_migration_helper.py`` can help to automate the
|
|
||||||
finding of code and templates that may need to be upgraded. It contains full
|
|
||||||
help on how to use it.
|
|
||||||
|
|
||||||
Finally, once all applications are upgraded, ``CsrfResponseMiddleware`` can be
|
|
||||||
removed from your settings.
|
|
||||||
|
|
||||||
While ``CsrfResponseMiddleware`` is still in use, the ``csrf_response_exempt``
|
|
||||||
decorator, described in `Exceptions`_, may be useful. The post-processing
|
|
||||||
middleware imposes a performance hit and a potential vulnerability, and any
|
|
||||||
views that have been upgraded to use the new template tag method no longer need
|
|
||||||
it.
|
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
----------
|
----------
|
||||||
|
|
||||||
.. versionchanged:: 1.2
|
|
||||||
Import paths for the decorators below were changed.
|
|
||||||
|
|
||||||
To manually exclude a view function from being handled by either of the two CSRF
|
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
|
middleware, you can use the ``csrf_exempt`` decorator, found in the
|
||||||
``django.views.decorators.csrf`` module. For example::
|
``django.views.decorators.csrf`` module. For example::
|
||||||
|
@ -295,13 +149,6 @@ middleware, you can use the ``csrf_exempt`` decorator, found in the
|
||||||
def my_view(request):
|
def my_view(request):
|
||||||
return HttpResponse('Hello world')
|
return HttpResponse('Hello world')
|
||||||
|
|
||||||
Like the middleware, the ``csrf_exempt`` decorator is composed of two parts: a
|
|
||||||
``csrf_view_exempt`` decorator and a ``csrf_response_exempt`` decorator, found
|
|
||||||
in the same module. These disable the view protection mechanism
|
|
||||||
(``CsrfViewMiddleware``) and the response post-processing
|
|
||||||
(``CsrfResponseMiddleware``) respectively. They can be used individually if
|
|
||||||
required.
|
|
||||||
|
|
||||||
Subdomains
|
Subdomains
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -350,8 +197,7 @@ The CSRF protection is based on the following things:
|
||||||
outgoing POST forms. The value of this field is the value of the CSRF
|
outgoing POST forms. The value of this field is the value of the CSRF
|
||||||
cookie.
|
cookie.
|
||||||
|
|
||||||
This part is done by the template tag (and with the legacy method, it is done
|
This part is done by the template tag.
|
||||||
by ``CsrfResponseMiddleware``).
|
|
||||||
|
|
||||||
3. For all incoming POST requests, a CSRF cookie must be present, and the
|
3. For all incoming POST requests, a CSRF cookie must be present, and the
|
||||||
'csrfmiddlewaretoken' field must be present and correct. If it isn't, the
|
'csrfmiddlewaretoken' field must be present and correct. If it isn't, the
|
||||||
|
@ -375,22 +221,16 @@ forms). GET requests ought never to have any potentially dangerous side effects
|
||||||
(see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET
|
(see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET
|
||||||
request ought to be harmless.
|
request ought to be harmless.
|
||||||
|
|
||||||
``CsrfResponseMiddleware`` checks the Content-Type before modifying the
|
|
||||||
response, and only pages that are served as 'text/html' or
|
|
||||||
'application/xml+xhtml' are modified.
|
|
||||||
|
|
||||||
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
||||||
|
|
||||||
Caching
|
Caching
|
||||||
=======
|
=======
|
||||||
|
|
||||||
If the :ttag:`csrf_token` template tag is used by a template (or the ``get_token``
|
If the :ttag:`csrf_token` template tag is used by a template (or the
|
||||||
function is called some other way), ``CsrfViewMiddleware`` will add a cookie and
|
``get_token`` function is called some other way), ``CsrfViewMiddleware`` will
|
||||||
a ``Vary: Cookie`` header to the response. Similarly,
|
add a cookie and a ``Vary: Cookie`` header to the response. This means that the
|
||||||
``CsrfResponseMiddleware`` will send the ``Vary: Cookie`` header if it inserted
|
middleware will play well with the cache middleware if it is used as instructed
|
||||||
a token. This means that these middleware will play well with the cache
|
(``UpdateCacheMiddleware`` goes before all other middleware).
|
||||||
middleware if it is used as instructed (``UpdateCacheMiddleware`` goes before
|
|
||||||
all other middleware).
|
|
||||||
|
|
||||||
However, if you use cache decorators on individual views, the CSRF middleware
|
However, if you use cache decorators on individual views, the CSRF middleware
|
||||||
will not yet have been able to set the Vary header. In this case, on any views
|
will not yet have been able to set the Vary header. In this case, on any views
|
||||||
|
@ -434,13 +274,6 @@ to set cookies). Note that even without CSRF, there are other vulnerabilities,
|
||||||
such as session fixation, that make giving subdomains to untrusted parties a bad
|
such as session fixation, that make giving subdomains to untrusted parties a bad
|
||||||
idea, and these vulnerabilities cannot easily be fixed with current browsers.
|
idea, and these vulnerabilities cannot easily be fixed with current browsers.
|
||||||
|
|
||||||
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.middleware.csrf.get_token` to get
|
|
||||||
the CSRF token and ensure it is included when your form is submitted.
|
|
||||||
|
|
||||||
Contrib and reusable apps
|
Contrib and reusable apps
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import warnings
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware
|
from django.middleware.csrf import CsrfViewMiddleware
|
||||||
from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, requires_csrf_token
|
from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, requires_csrf_token
|
||||||
from django.core.context_processors import csrf
|
from django.core.context_processors import csrf
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -16,11 +16,6 @@ def post_form_response():
|
||||||
""", mimetype="text/html")
|
""", mimetype="text/html")
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def post_form_response_non_html():
|
|
||||||
resp = post_form_response()
|
|
||||||
resp["Content-Type"] = "application/xml"
|
|
||||||
return resp
|
|
||||||
|
|
||||||
def post_form_view(request):
|
def post_form_view(request):
|
||||||
"""A view that returns a POST form (without a token)"""
|
"""A view that returns a POST form (without a token)"""
|
||||||
return post_form_response()
|
return post_form_response()
|
||||||
|
@ -54,7 +49,7 @@ class TestingHttpRequest(HttpRequest):
|
||||||
def is_secure(self):
|
def is_secure(self):
|
||||||
return getattr(self, '_is_secure', False)
|
return getattr(self, '_is_secure', False)
|
||||||
|
|
||||||
class CsrfMiddlewareTest(TestCase):
|
class CsrfViewMiddlewareTest(TestCase):
|
||||||
# The csrf token is potentially from an untrusted source, so could have
|
# The csrf token is potentially from an untrusted source, so could have
|
||||||
# characters that need dealing with.
|
# characters that need dealing with.
|
||||||
_csrf_id_cookie = "<1>\xc2\xa1"
|
_csrf_id_cookie = "<1>\xc2\xa1"
|
||||||
|
@ -94,51 +89,12 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
def _check_token_present(self, response, csrf_id=None):
|
def _check_token_present(self, response, csrf_id=None):
|
||||||
self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
|
self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
|
||||||
|
|
||||||
# Check the post processing and outgoing cookie
|
def test_process_response_get_token_used(self):
|
||||||
def test_process_response_no_csrf_cookie(self):
|
|
||||||
"""
|
"""
|
||||||
When no prior CSRF cookie exists, check that the cookie is created and a
|
When get_token is used, check that the cookie is created and headers
|
||||||
token is inserted.
|
patched.
|
||||||
"""
|
"""
|
||||||
req = self._get_GET_no_csrf_cookie_request()
|
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.assertTrue('Cookie' in resp2.get('Vary',''))
|
|
||||||
|
|
||||||
def test_process_response_for_exempt_view(self):
|
|
||||||
"""
|
|
||||||
Check that a view decorated with 'csrf_view_exempt' is still
|
|
||||||
post-processed to add the CSRF token.
|
|
||||||
"""
|
|
||||||
req = self._get_GET_no_csrf_cookie_request()
|
|
||||||
CsrfMiddleware().process_view(req, csrf_view_exempt(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)
|
|
||||||
|
|
||||||
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
|
# token_view calls get_token() indirectly
|
||||||
CsrfViewMiddleware().process_view(req, token_view, (), {})
|
CsrfViewMiddleware().process_view(req, token_view, (), {})
|
||||||
resp = token_view(req)
|
resp = token_view(req)
|
||||||
|
@ -146,6 +102,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
|
|
||||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
|
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
|
||||||
self.assertNotEqual(csrf_cookie, False)
|
self.assertNotEqual(csrf_cookie, False)
|
||||||
|
self.assertTrue('Cookie' in resp2.get('Vary',''))
|
||||||
|
|
||||||
def test_process_response_get_token_not_used(self):
|
def test_process_response_get_token_not_used(self):
|
||||||
"""
|
"""
|
||||||
|
@ -167,43 +124,6 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
|
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
|
||||||
self.assertEqual(csrf_cookie, 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.assertEqual(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_GET_csrf_cookie_request()
|
|
||||||
view = csrf_exempt(post_form_view)
|
|
||||||
CsrfMiddleware().process_view(req, view, (), {})
|
|
||||||
|
|
||||||
resp = view(req)
|
|
||||||
resp_content = resp.content
|
|
||||||
resp2 = CsrfMiddleware().process_response(req, resp)
|
|
||||||
self.assertEqual(resp_content, resp2.content)
|
|
||||||
|
|
||||||
# Check the request processing
|
# Check the request processing
|
||||||
def test_process_request_no_csrf_cookie(self):
|
def test_process_request_no_csrf_cookie(self):
|
||||||
"""
|
"""
|
||||||
|
@ -211,7 +131,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
incoming request. This will stop login CSRF.
|
incoming request. This will stop login CSRF.
|
||||||
"""
|
"""
|
||||||
req = self._get_POST_no_csrf_cookie_request()
|
req = self._get_POST_no_csrf_cookie_request()
|
||||||
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
self.assertEqual(403, req2.status_code)
|
self.assertEqual(403, req2.status_code)
|
||||||
|
|
||||||
def test_process_request_csrf_cookie_no_token(self):
|
def test_process_request_csrf_cookie_no_token(self):
|
||||||
|
@ -220,7 +140,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
rejects the incoming request.
|
rejects the incoming request.
|
||||||
"""
|
"""
|
||||||
req = self._get_POST_csrf_cookie_request()
|
req = self._get_POST_csrf_cookie_request()
|
||||||
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
self.assertEqual(403, req2.status_code)
|
self.assertEqual(403, req2.status_code)
|
||||||
|
|
||||||
def test_process_request_csrf_cookie_and_token(self):
|
def test_process_request_csrf_cookie_and_token(self):
|
||||||
|
@ -228,7 +148,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
Check that if both a cookie and a token is present, the middleware lets it through.
|
Check that if both a cookie and a token is present, the middleware lets it through.
|
||||||
"""
|
"""
|
||||||
req = self._get_POST_request_with_token()
|
req = self._get_POST_request_with_token()
|
||||||
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
self.assertEqual(None, req2)
|
self.assertEqual(None, req2)
|
||||||
|
|
||||||
def test_process_request_csrf_cookie_no_token_exempt_view(self):
|
def test_process_request_csrf_cookie_no_token_exempt_view(self):
|
||||||
|
@ -237,7 +157,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
decorator has been applied to the view, the middleware lets it through
|
decorator has been applied to the view, the middleware lets it through
|
||||||
"""
|
"""
|
||||||
req = self._get_POST_csrf_cookie_request()
|
req = self._get_POST_csrf_cookie_request()
|
||||||
req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
|
req2 = CsrfViewMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
|
||||||
self.assertEqual(None, req2)
|
self.assertEqual(None, req2)
|
||||||
|
|
||||||
def test_csrf_token_in_header(self):
|
def test_csrf_token_in_header(self):
|
||||||
|
@ -246,7 +166,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
"""
|
"""
|
||||||
req = self._get_POST_csrf_cookie_request()
|
req = self._get_POST_csrf_cookie_request()
|
||||||
req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id
|
req.META['HTTP_X_CSRFTOKEN'] = self._csrf_id
|
||||||
req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
self.assertEqual(None, req2)
|
self.assertEqual(None, req2)
|
||||||
|
|
||||||
# Tests for the template tag method
|
# Tests for the template tag method
|
||||||
|
@ -307,15 +227,6 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME]
|
csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME]
|
||||||
self._check_token_present(resp, csrf_id=csrf_cookie.value)
|
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):
|
def test_https_bad_referer(self):
|
||||||
"""
|
"""
|
||||||
Test that a POST HTTPS request with a bad referer is rejected
|
Test that a POST HTTPS request with a bad referer is rejected
|
||||||
|
|
Loading…
Reference in New Issue