477 lines
18 KiB
Plaintext
477 lines
18 KiB
Plaintext
=====================================
|
|
Cross Site Request Forgery protection
|
|
=====================================
|
|
|
|
.. module:: django.middleware.csrf
|
|
:synopsis: Protects against Cross Site Request Forgeries
|
|
|
|
The CSRF middleware and template tag provides easy-to-use protection against
|
|
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
|
|
Web site contains a link, a form button or some javascript that is intended to
|
|
perform some action on your Web site, using the credentials of a logged-in user
|
|
who visits the malicious site in their browser. A related type of attack,
|
|
'login CSRF', where an attacking site tricks a user's browser into logging into
|
|
a site with someone else's credentials, is also covered.
|
|
|
|
The first defense against CSRF attacks is to ensure that GET requests (and other
|
|
'safe' methods, as defined by `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_) are
|
|
side-effect free. Requests via 'unsafe' methods, such as POST, PUT and DELETE,
|
|
can then be protected by following the steps below.
|
|
|
|
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
|
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
|
|
|
.. _using-csrf:
|
|
|
|
How to use it
|
|
=============
|
|
|
|
To enable CSRF protection for your views, follow these steps:
|
|
|
|
1. Add the middleware
|
|
``'django.middleware.csrf.CsrfViewMiddleware'`` to your list of
|
|
middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come
|
|
and before any view middleware that assume that CSRF attacks have
|
|
been dealt with.)
|
|
|
|
Alternatively, you can use the decorator
|
|
:func:`~django.views.decorators.csrf.csrf_protect` on particular views
|
|
you want to protect (see below).
|
|
|
|
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.::
|
|
|
|
<form action="." method="post">{% csrf_token %}
|
|
|
|
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.
|
|
|
|
3. In the corresponding view functions, ensure that the
|
|
``'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.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.
|
|
|
|
2. Manually import and use the processor to generate the CSRF token and
|
|
add it to the template context. e.g.::
|
|
|
|
from django.core.context_processors import csrf
|
|
from django.shortcuts import render_to_response
|
|
|
|
def my_view(request):
|
|
c = {}
|
|
c.update(csrf(request))
|
|
# ... view code here
|
|
return render_to_response("a_template.html", c)
|
|
|
|
You may want to write your own
|
|
:func:`~django.shortcuts.render_to_response()` wrapper that takes care
|
|
of this step for you.
|
|
|
|
The utility script ``extras/csrf_migration_helper.py`` can help to automate the
|
|
finding of code and templates that may need these steps. It contains full help
|
|
on how to use it.
|
|
|
|
.. _csrf-ajax:
|
|
|
|
AJAX
|
|
----
|
|
|
|
While the above method can be used for AJAX POST requests, it has some
|
|
inconveniences: you have to remember to pass the CSRF token in as POST data with
|
|
every POST request. For this reason, there is an alternative method: on each
|
|
XMLHttpRequest, set a custom `X-CSRFToken` header to the value of the CSRF
|
|
token. This is often easier, because many javascript frameworks provide hooks
|
|
that allow headers to be set on every request. In jQuery, you can use the
|
|
``ajaxSend`` event as follows:
|
|
|
|
.. code-block:: javascript
|
|
|
|
$(document).ajaxSend(function(event, xhr, settings) {
|
|
function getCookie(name) {
|
|
var cookieValue = null;
|
|
if (document.cookie && document.cookie != '') {
|
|
var cookies = document.cookie.split(';');
|
|
for (var i = 0; i < cookies.length; i++) {
|
|
var cookie = jQuery.trim(cookies[i]);
|
|
// Does this cookie string begin with the name we want?
|
|
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cookieValue;
|
|
}
|
|
function sameOrigin(url) {
|
|
// url could be relative or scheme relative or absolute
|
|
var host = document.location.host; // host + port
|
|
var protocol = document.location.protocol;
|
|
var sr_origin = '//' + host;
|
|
var origin = protocol + sr_origin;
|
|
// Allow absolute or scheme relative URLs to same origin
|
|
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
|
|
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
|
|
// or any other URL that isn't scheme relative or absolute i.e relative.
|
|
!(/^(\/\/|http:|https:).*/.test(url));
|
|
}
|
|
function safeMethod(method) {
|
|
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
|
}
|
|
|
|
if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
|
|
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
|
|
}
|
|
});
|
|
|
|
.. note::
|
|
|
|
Due to a bug introduced in jQuery 1.5, the example above will not work
|
|
correctly on that version. Make sure you are running at least jQuery 1.5.1.
|
|
|
|
Adding this to a javascript file that is included on your site will ensure that
|
|
AJAX POST requests that are made via jQuery will not be caught by the CSRF
|
|
protection.
|
|
|
|
The above code could be simplified by using the `jQuery cookie plugin
|
|
<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``, and
|
|
`settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in jQuery 1.5 and
|
|
later to replace ``sameOrigin``.
|
|
|
|
In addition, if the CSRF cookie has not been sent to the client by use of
|
|
:ttag:`csrf_token`, you may need to ensure the client receives the cookie by
|
|
using :func:`~django.views.decorators.csrf.ensure_csrf_cookie`.
|
|
|
|
The decorator method
|
|
--------------------
|
|
|
|
.. module:: django.views.decorators.csrf
|
|
|
|
Rather than adding ``CsrfViewMiddleware`` as a blanket protection, you can use
|
|
the ``csrf_protect`` decorator, which has exactly the same functionality, on
|
|
particular views that need the protection. It must be used **both** on views
|
|
that insert the CSRF token in the output, and on those that accept the POST form
|
|
data. (These are often the same view function, but not always).
|
|
|
|
Use of the decorator by itself is **not recommended**, 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.
|
|
|
|
.. function:: csrf_protect(view)
|
|
|
|
Decorator that provides the protection of ``CsrfViewMiddleware`` to a view.
|
|
|
|
Usage::
|
|
|
|
from django.views.decorators.csrf import csrf_protect
|
|
from django.shortcuts import render
|
|
|
|
@csrf_protect
|
|
def my_view(request):
|
|
c = {}
|
|
# ...
|
|
return render(request, "a_template.html", c)
|
|
|
|
Rejected requests
|
|
=================
|
|
|
|
By default, a '403 Forbidden' response is sent to the user if an incoming
|
|
request fails the checks performed by ``CsrfViewMiddleware``. This should
|
|
usually only be seen when there is a genuine Cross Site Request Forgery, or
|
|
when, due to a programming error, the CSRF token has not been included with a
|
|
POST form.
|
|
|
|
The error page, however, is not very friendly, so you may want to provide your
|
|
own view for handling this condition. To do this, simply set the
|
|
:setting:`CSRF_FAILURE_VIEW` setting.
|
|
|
|
.. _how-csrf-works:
|
|
|
|
How it works
|
|
============
|
|
|
|
The CSRF protection is based on the following things:
|
|
|
|
1. A CSRF cookie that is set to a random value (a session independent nonce, as
|
|
it is called), which other sites will not have access to.
|
|
|
|
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.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
|
|
outgoing POST forms. The value of this field is the value of the CSRF
|
|
cookie.
|
|
|
|
This part is done by the template tag.
|
|
|
|
3. For all incoming requests that are not using HTTP GET, HEAD, OPTIONS or
|
|
TRACE, a CSRF cookie must be present, and the 'csrfmiddlewaretoken' field
|
|
must be present and correct. If it isn't, the user will get a 403 error.
|
|
|
|
This check is done by ``CsrfViewMiddleware``.
|
|
|
|
4. In addition, for HTTPS requests, strict referer checking is done by
|
|
``CsrfViewMiddleware``. This is necessary to address a Man-In-The-Middle
|
|
attack that is possible under HTTPS when using a session independent nonce,
|
|
due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted
|
|
by clients that are talking to a site under HTTPS. (Referer checking is not
|
|
done for HTTP requests because the presence of the Referer header is not
|
|
reliable enough under HTTP.)
|
|
|
|
This ensures that only forms that have originated from your Web site can be used
|
|
to POST data back.
|
|
|
|
It deliberately ignores GET requests (and other requests that are defined as
|
|
'safe' by RFC 2616). These requests ought never to have any potentially
|
|
dangerous side effects , and so a CSRF attack with a GET request ought to be
|
|
harmless. RFC 2616 defines POST, PUT and DELETE as 'unsafe', and all other
|
|
methods are assumed to be unsafe, for maximum protection.
|
|
|
|
Caching
|
|
=======
|
|
|
|
If the :ttag:`csrf_token` template tag is used by a template (or the
|
|
``get_token`` function is called some other way), ``CsrfViewMiddleware`` will
|
|
add a cookie and a ``Vary: Cookie`` header to the response. This means that the
|
|
middleware will play well with the cache 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
|
|
will not yet have been able to set the Vary header or the CSRF cookie, and the
|
|
response will be cached without either one. In this case, on any views that
|
|
will require a CSRF token to be inserted you should use the
|
|
:func:`django.views.decorators.csrf.csrf_protect` decorator first::
|
|
|
|
from django.views.decorators.cache import cache_page
|
|
from django.views.decorators.csrf import csrf_protect
|
|
|
|
@cache_page(60 * 15)
|
|
@csrf_protect
|
|
def my_view(request):
|
|
# ...
|
|
|
|
|
|
Testing
|
|
=======
|
|
|
|
The ``CsrfViewMiddleware`` will usually be a big hindrance to testing view
|
|
functions, due to the need for the CSRF token which must be sent with every POST
|
|
request. For this reason, Django's HTTP client for tests has been modified to
|
|
set a flag on requests which relaxes the middleware and the ``csrf_protect``
|
|
decorator so that they no longer rejects requests. In every other respect
|
|
(e.g. sending cookies etc.), they behave the same.
|
|
|
|
If, for some reason, you *want* the test client to perform CSRF
|
|
checks, you can create an instance of the test client that enforces
|
|
CSRF checks::
|
|
|
|
>>> from django.test import Client
|
|
>>> csrf_client = Client(enforce_csrf_checks=True)
|
|
|
|
.. _csrf-limitations:
|
|
|
|
Limitations
|
|
===========
|
|
|
|
Subdomains within a site will be able to set cookies on the client for the whole
|
|
domain. By setting the cookie and using a corresponding token, subdomains will
|
|
be able to circumvent the CSRF protection. The only way to avoid this is to
|
|
ensure that subdomains are controlled by trusted users (or, are at least unable
|
|
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
|
|
idea, and these vulnerabilities cannot easily be fixed with current browsers.
|
|
|
|
Edge cases
|
|
==========
|
|
|
|
Certain views can have unusual requirements that mean they don't fit the normal
|
|
pattern envisaged here. A number of utilities can be useful in these
|
|
situations. The scenarios they might be needed in are described in the following
|
|
section.
|
|
|
|
Utilities
|
|
---------
|
|
|
|
.. function:: csrf_exempt(view)
|
|
|
|
This decorator marks a view as being exempt from the protection ensured by
|
|
the middleware. Example::
|
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
@csrf_exempt
|
|
def my_view(request):
|
|
return HttpResponse('Hello world')
|
|
|
|
.. function:: requires_csrf_token(view)
|
|
|
|
Normally the :ttag:`csrf_token` template tag will not work if
|
|
``CsrfViewMiddleware.process_view`` or an equivalent like ``csrf_protect``
|
|
has not run. The view decorator ``requires_csrf_token`` can be used to
|
|
ensure the template tag does work. This decorator works similarly to
|
|
``csrf_protect``, but never rejects an incoming request.
|
|
|
|
Example::
|
|
|
|
from django.views.decorators.csrf import requires_csrf_token
|
|
from django.shortcuts import render
|
|
|
|
@requires_csrf_token
|
|
def my_view(request):
|
|
c = {}
|
|
# ...
|
|
return render(request, "a_template.html", c)
|
|
|
|
.. function:: ensure_csrf_cookie(view)
|
|
|
|
This decorator forces a view to send the CSRF cookie.
|
|
|
|
Scenarios
|
|
---------
|
|
|
|
CSRF protection should be disabled for just a few views
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Most views requires CSRF protection, but a few do not.
|
|
|
|
Solution: rather than disabling the middleware and applying ``csrf_protect`` to
|
|
all the views that need it, enable the middleware and use
|
|
:func:`~django.views.decorators.csrf.csrf_exempt`.
|
|
|
|
CsrfViewMiddleware.process_view not used
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There are cases when may not have run before your view is run - 404 and 500
|
|
handlers, for example - but you still need the CSRF token in a form.
|
|
|
|
Solution: use :func:`~django.views.decorators.csrf.requires_csrf_token`
|
|
|
|
Unprotected view needs the CSRF token
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There may be some views that are unprotected and have been exempted by
|
|
``csrf_exempt``, but still need to include the CSRF token.
|
|
|
|
Solution: use :func:`~django.views.decorators.csrf.csrf_exempt` followed by
|
|
:func:`~django.views.decorators.csrf.requires_csrf_token`. (i.e. ``requires_csrf_token``
|
|
should be the innermost decorator).
|
|
|
|
View needs protection for one path
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A view needs CRSF protection under one set of conditions only, and mustn't have
|
|
it for the rest of the time.
|
|
|
|
Solution: use :func:`~django.views.decorators.csrf.csrf_exempt` for the whole
|
|
view function, and :func:`~django.views.decorators.csrf.csrf_protect` for the
|
|
path within it that needs protection. Example::
|
|
|
|
from django.views.decorators.csrf import csrf_exempt, csrf_protect
|
|
|
|
@csrf_exempt
|
|
def my_view(request):
|
|
|
|
@csrf_protect
|
|
def protected_path(request):
|
|
do_something()
|
|
|
|
if some_condition():
|
|
return protected_path(request)
|
|
else:
|
|
do_something_else()
|
|
|
|
Page uses AJAX without any HTML form
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A page makes a POST request via AJAX, and the page does not have an HTML form
|
|
with a :ttag:`csrf_token` that would cause the required CSRF cookie to be sent.
|
|
|
|
Solution: use :func:`~django.views.decorators.csrf.ensure_csrf_cookie` on the
|
|
view that sends the page.
|
|
|
|
Contrib and reusable apps
|
|
=========================
|
|
|
|
Because it is possible for the developer to turn off the ``CsrfViewMiddleware``,
|
|
all relevant views in contrib apps use the ``csrf_protect`` decorator to ensure
|
|
the security of these applications against CSRF. It is recommended that the
|
|
developers of other reusable apps that want the same guarantees also use the
|
|
``csrf_protect`` decorator on their views.
|
|
|
|
Settings
|
|
========
|
|
|
|
A number of settings can be used to control Django's CSRF behavior.
|
|
|
|
CSRF_COOKIE_DOMAIN
|
|
------------------
|
|
|
|
.. versionadded:: 1.2
|
|
|
|
Default: ``None``
|
|
|
|
The domain to be used when setting the CSRF cookie. This can be useful for
|
|
easily allowing cross-subdomain requests to be exluded from the normal cross
|
|
site request forgery protection. It should be set to a string such as
|
|
``".lawrence.com"`` to allow a POST request from a form on one subdomain to be
|
|
accepted by accepted by a view served from another subdomain.
|
|
|
|
Please note that, with or without use of this setting, this CSRF protection
|
|
mechanism is not safe against cross-subdomain attacks -- see `Limitations`_.
|
|
|
|
CSRF_COOKIE_NAME
|
|
----------------
|
|
|
|
.. versionadded:: 1.2
|
|
|
|
Default: ``'csrftoken'``
|
|
|
|
The name of the cookie to use for the CSRF authentication token. This can be
|
|
whatever you want.
|
|
|
|
CSRF_COOKIE_PATH
|
|
----------------
|
|
|
|
.. versionadded:: 1.4
|
|
|
|
Default: ``'/'``
|
|
|
|
The path set on the CSRF cookie. This should either match the URL path of your
|
|
Django installation or be a parent of that path.
|
|
|
|
This is useful if you have multiple Django instances running under the same
|
|
hostname. They can use different cookie paths, and each instance will only see
|
|
its own CSRF cookie.
|
|
|
|
CSRF_COOKIE_SECURE
|
|
------------------
|
|
|
|
.. versionadded:: 1.4
|
|
|
|
Default: ``False``
|
|
|
|
Whether to use a secure cookie for the CSRF cookie. If this is set to ``True``,
|
|
the cookie will be marked as "secure," which means browsers may ensure that the
|
|
cookie is only sent under an HTTPS connection.
|
|
|
|
CSRF_FAILURE_VIEW
|
|
-----------------
|
|
|
|
.. versionadded:: 1.2
|
|
|
|
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::
|
|
|
|
def csrf_failure(request, reason="")
|
|
|
|
where ``reason`` is a short message (intended for developers or logging, not for
|
|
end users) indicating the reason the request was rejected.
|