Refs #28592 -- Created a new CSRF how-to document.
This commit is contained in:
parent
8e63390640
commit
e458abc593
|
@ -0,0 +1,309 @@
|
||||||
|
.. _using-csrf:
|
||||||
|
|
||||||
|
===================================
|
||||||
|
How to use Django's CSRF protection
|
||||||
|
===================================
|
||||||
|
|
||||||
|
To take advantage of CSRF protection in your views, follow these steps:
|
||||||
|
|
||||||
|
#. The CSRF middleware is activated by default in the :setting:`MIDDLEWARE`
|
||||||
|
setting. If you override that setting, remember that
|
||||||
|
``'django.middleware.csrf.CsrfViewMiddleware'`` should come before any view
|
||||||
|
middleware that assume that CSRF attacks have been dealt with.
|
||||||
|
|
||||||
|
If you disabled it, which is not recommended, you can use
|
||||||
|
:func:`~django.views.decorators.csrf.csrf_protect` on particular views
|
||||||
|
you want to protect (see below).
|
||||||
|
|
||||||
|
#. 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.:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
<form 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.
|
||||||
|
|
||||||
|
#. In the corresponding view functions, ensure that
|
||||||
|
:class:`~django.template.RequestContext` is used to render the response so
|
||||||
|
that ``{% csrf_token %}`` will work properly. If you're using the
|
||||||
|
:func:`~django.shortcuts.render` function, generic views, or contrib apps,
|
||||||
|
you are covered already since these all use ``RequestContext``.
|
||||||
|
|
||||||
|
.. _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 (as specified by the
|
||||||
|
:setting:`CSRF_HEADER_NAME` setting) 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.
|
||||||
|
|
||||||
|
First, you must get the CSRF token. How to do that depends on whether or not
|
||||||
|
the :setting:`CSRF_USE_SESSIONS` and :setting:`CSRF_COOKIE_HTTPONLY` settings
|
||||||
|
are enabled.
|
||||||
|
|
||||||
|
.. _acquiring-csrf-token-from-cookie:
|
||||||
|
|
||||||
|
Acquiring the token if :setting:`CSRF_USE_SESSIONS` and :setting:`CSRF_COOKIE_HTTPONLY` are ``False``
|
||||||
|
-----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The recommended source for the token is the ``csrftoken`` cookie, which will be
|
||||||
|
set if you've enabled CSRF protection for your views as outlined above.
|
||||||
|
|
||||||
|
The CSRF token cookie is named ``csrftoken`` by default, but you can control
|
||||||
|
the cookie name via the :setting:`CSRF_COOKIE_NAME` setting.
|
||||||
|
|
||||||
|
You can acquire the token like this:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
let cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== '') {
|
||||||
|
const cookies = document.cookie.split(';');
|
||||||
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
const cookie = cookies[i].trim();
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
const csrftoken = getCookie('csrftoken');
|
||||||
|
|
||||||
|
The above code could be simplified by using the `JavaScript Cookie library
|
||||||
|
<https://github.com/js-cookie/js-cookie/>`_ to replace ``getCookie``:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
const csrftoken = Cookies.get('csrftoken');
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The CSRF token is also present in the DOM in a masked form, but only if
|
||||||
|
explicitly included using :ttag:`csrf_token` in a template. The cookie
|
||||||
|
contains the canonical, unmasked token. The
|
||||||
|
:class:`~django.middleware.csrf.CsrfViewMiddleware` will accept either.
|
||||||
|
However, in order to protect against `BREACH`_ attacks, it's recommended to
|
||||||
|
use a masked token.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
If your view is not rendering a template containing the :ttag:`csrf_token`
|
||||||
|
template tag, Django might not set the CSRF token cookie. This is common in
|
||||||
|
cases where forms are dynamically added to the page. To address this case,
|
||||||
|
Django provides a view decorator which forces setting of the cookie:
|
||||||
|
:func:`~django.views.decorators.csrf.ensure_csrf_cookie`.
|
||||||
|
|
||||||
|
.. _BREACH: http://breachattack.com/
|
||||||
|
|
||||||
|
.. _acquiring-csrf-token-from-html:
|
||||||
|
|
||||||
|
Acquiring the token if :setting:`CSRF_USE_SESSIONS` or :setting:`CSRF_COOKIE_HTTPONLY` is ``True``
|
||||||
|
--------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
If you activate :setting:`CSRF_USE_SESSIONS` or
|
||||||
|
:setting:`CSRF_COOKIE_HTTPONLY`, you must include the CSRF token in your HTML
|
||||||
|
and read the token from the DOM with JavaScript:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
{% csrf_token %}
|
||||||
|
<script>
|
||||||
|
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Setting the token on the AJAX request
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
Finally, you'll need to set the header on your AJAX request. Using the
|
||||||
|
`fetch()`_ API:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
const request = new Request(
|
||||||
|
/* URL */,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'X-CSRFToken': csrftoken},
|
||||||
|
mode: 'same-origin' // Do not send CSRF token to another domain.
|
||||||
|
}
|
||||||
|
);
|
||||||
|
fetch(request).then(function(response) {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
.. _fetch(): https://developer.mozilla.org/en-US/docs/Web/API/fetch
|
||||||
|
|
||||||
|
Using CSRF protection in Jinja2 templates
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
Django's :class:`~django.template.backends.jinja2.Jinja2` template backend
|
||||||
|
adds ``{{ csrf_input }}`` to the context of all templates which is equivalent
|
||||||
|
to ``{% csrf_token %}`` in the Django template language. For example:
|
||||||
|
|
||||||
|
.. code-block:: html+jinja
|
||||||
|
|
||||||
|
<form method="post">{{ csrf_input }}
|
||||||
|
|
||||||
|
Using the decorator method
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Rather than adding ``CsrfViewMiddleware`` as a blanket protection, you can use
|
||||||
|
the :func:`~django.views.decorators.csrf.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.
|
||||||
|
|
||||||
|
.. _csrf-rejected-requests:
|
||||||
|
|
||||||
|
Handle 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, set the
|
||||||
|
:setting:`CSRF_FAILURE_VIEW` setting.
|
||||||
|
|
||||||
|
CSRF failures are logged as warnings to the :ref:`django.security.csrf
|
||||||
|
<django-security-logger>` logger.
|
||||||
|
|
||||||
|
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):
|
||||||
|
...
|
||||||
|
|
||||||
|
If you are using class-based views, you can refer to :ref:`Decorating
|
||||||
|
class-based views<decorating-class-based-views>`.
|
||||||
|
|
||||||
|
Testing and CSRF protection
|
||||||
|
===========================
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Disabling CSRF protection 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`.
|
||||||
|
|
||||||
|
Setting the token when CsrfViewMiddleware.process_view is not used
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
There are cases when ``CsrfViewMiddleware.process_view`` 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`
|
||||||
|
|
||||||
|
Including the CSRF token in an unprotected view
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
Protecting a view for only one path
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
A view needs CSRF 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()
|
||||||
|
|
||||||
|
Protecting a page that uses AJAX without an 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.
|
|
@ -11,6 +11,7 @@ you quickly accomplish common tasks.
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
auth-remote-user
|
auth-remote-user
|
||||||
|
csrf
|
||||||
custom-management-commands
|
custom-management-commands
|
||||||
custom-model-fields
|
custom-model-fields
|
||||||
custom-lookups
|
custom-lookups
|
||||||
|
|
|
@ -16,215 +16,10 @@ a site with someone else's credentials, is also covered.
|
||||||
The first defense against CSRF attacks is to ensure that GET requests (and other
|
The first defense against CSRF attacks is to ensure that GET requests (and other
|
||||||
'safe' methods, as defined by :rfc:`7231#section-4.2.1`) are side effect free.
|
'safe' methods, as defined by :rfc:`7231#section-4.2.1`) are side effect free.
|
||||||
Requests via 'unsafe' methods, such as POST, PUT, and DELETE, can then be
|
Requests via 'unsafe' methods, such as POST, PUT, and DELETE, can then be
|
||||||
protected by following the steps below.
|
protected by the steps outlined in :ref:`using-csrf`.
|
||||||
|
|
||||||
.. _Cross Site Request Forgeries: https://www.squarefree.com/securitytips/web-developers.html#CSRF
|
.. _Cross Site Request Forgeries: https://www.squarefree.com/securitytips/web-developers.html#CSRF
|
||||||
|
|
||||||
.. _using-csrf:
|
|
||||||
|
|
||||||
How to use it
|
|
||||||
=============
|
|
||||||
|
|
||||||
To take advantage of CSRF protection in your views, follow these steps:
|
|
||||||
|
|
||||||
#. The CSRF middleware is activated by default in the :setting:`MIDDLEWARE`
|
|
||||||
setting. If you override that setting, remember that
|
|
||||||
``'django.middleware.csrf.CsrfViewMiddleware'`` should come before any view
|
|
||||||
middleware that assume that CSRF attacks have been dealt with.
|
|
||||||
|
|
||||||
If you disabled it, which is not recommended, you can use
|
|
||||||
:func:`~django.views.decorators.csrf.csrf_protect` on particular views
|
|
||||||
you want to protect (see below).
|
|
||||||
|
|
||||||
#. 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.:
|
|
||||||
|
|
||||||
.. code-block:: html+django
|
|
||||||
|
|
||||||
<form 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.
|
|
||||||
|
|
||||||
#. In the corresponding view functions, ensure that
|
|
||||||
:class:`~django.template.RequestContext` is used to render the response so
|
|
||||||
that ``{% csrf_token %}`` will work properly. If you're using the
|
|
||||||
:func:`~django.shortcuts.render` function, generic views, or contrib apps,
|
|
||||||
you are covered already since these all use ``RequestContext``.
|
|
||||||
|
|
||||||
.. _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 (as specified by the
|
|
||||||
:setting:`CSRF_HEADER_NAME` setting) 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.
|
|
||||||
|
|
||||||
First, you must get the CSRF token. How to do that depends on whether or not
|
|
||||||
the :setting:`CSRF_USE_SESSIONS` and :setting:`CSRF_COOKIE_HTTPONLY` settings
|
|
||||||
are enabled.
|
|
||||||
|
|
||||||
.. _acquiring-csrf-token-from-cookie:
|
|
||||||
|
|
||||||
Acquiring the token if :setting:`CSRF_USE_SESSIONS` and :setting:`CSRF_COOKIE_HTTPONLY` are ``False``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The recommended source for the token is the ``csrftoken`` cookie, which will be
|
|
||||||
set if you've enabled CSRF protection for your views as outlined above.
|
|
||||||
|
|
||||||
The CSRF token cookie is named ``csrftoken`` by default, but you can control
|
|
||||||
the cookie name via the :setting:`CSRF_COOKIE_NAME` setting.
|
|
||||||
|
|
||||||
You can acquire the token like this:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
function getCookie(name) {
|
|
||||||
let cookieValue = null;
|
|
||||||
if (document.cookie && document.cookie !== '') {
|
|
||||||
const cookies = document.cookie.split(';');
|
|
||||||
for (let i = 0; i < cookies.length; i++) {
|
|
||||||
const cookie = cookies[i].trim();
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
const csrftoken = getCookie('csrftoken');
|
|
||||||
|
|
||||||
The above code could be simplified by using the `JavaScript Cookie library
|
|
||||||
<https://github.com/js-cookie/js-cookie/>`_ to replace ``getCookie``:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
const csrftoken = Cookies.get('csrftoken');
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The CSRF token is also present in the DOM in a masked form, but only if
|
|
||||||
explicitly included using :ttag:`csrf_token` in a template. The cookie
|
|
||||||
contains the canonical, unmasked token. The
|
|
||||||
:class:`~django.middleware.csrf.CsrfViewMiddleware` will accept either.
|
|
||||||
However, in order to protect against `BREACH`_ attacks, it's recommended to
|
|
||||||
use a masked token.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
If your view is not rendering a template containing the :ttag:`csrf_token`
|
|
||||||
template tag, Django might not set the CSRF token cookie. This is common in
|
|
||||||
cases where forms are dynamically added to the page. To address this case,
|
|
||||||
Django provides a view decorator which forces setting of the cookie:
|
|
||||||
:func:`~django.views.decorators.csrf.ensure_csrf_cookie`.
|
|
||||||
|
|
||||||
.. _acquiring-csrf-token-from-html:
|
|
||||||
|
|
||||||
Acquiring the token if :setting:`CSRF_USE_SESSIONS` or :setting:`CSRF_COOKIE_HTTPONLY` is ``True``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you activate :setting:`CSRF_USE_SESSIONS` or
|
|
||||||
:setting:`CSRF_COOKIE_HTTPONLY`, you must include the CSRF token in your HTML
|
|
||||||
and read the token from the DOM with JavaScript:
|
|
||||||
|
|
||||||
.. code-block:: html+django
|
|
||||||
|
|
||||||
{% csrf_token %}
|
|
||||||
<script>
|
|
||||||
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
Setting the token on the AJAX request
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Finally, you'll need to set the header on your AJAX request. Using the
|
|
||||||
`fetch()`_ API:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
const request = new Request(
|
|
||||||
/* URL */,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {'X-CSRFToken': csrftoken},
|
|
||||||
mode: 'same-origin' // Do not send CSRF token to another domain.
|
|
||||||
}
|
|
||||||
);
|
|
||||||
fetch(request).then(function(response) {
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
|
|
||||||
.. _fetch(): https://developer.mozilla.org/en-US/docs/Web/API/fetch
|
|
||||||
|
|
||||||
Using CSRF in Jinja2 templates
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Django's :class:`~django.template.backends.jinja2.Jinja2` template backend
|
|
||||||
adds ``{{ csrf_input }}`` to the context of all templates which is equivalent
|
|
||||||
to ``{% csrf_token %}`` in the Django template language. For example:
|
|
||||||
|
|
||||||
.. code-block:: html+jinja
|
|
||||||
|
|
||||||
<form method="post">{{ csrf_input }}
|
|
||||||
|
|
||||||
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.shortcuts import render
|
|
||||||
from django.views.decorators.csrf import csrf_protect
|
|
||||||
|
|
||||||
@csrf_protect
|
|
||||||
def my_view(request):
|
|
||||||
c = {}
|
|
||||||
# ...
|
|
||||||
return render(request, "a_template.html", c)
|
|
||||||
|
|
||||||
If you are using class-based views, you can refer to
|
|
||||||
:ref:`Decorating class-based views<decorating-class-based-views>`.
|
|
||||||
|
|
||||||
.. _csrf-rejected-requests:
|
|
||||||
|
|
||||||
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, set the
|
|
||||||
:setting:`CSRF_FAILURE_VIEW` setting.
|
|
||||||
|
|
||||||
CSRF failures are logged as warnings to the :ref:`django.security.csrf
|
|
||||||
<django-security-logger>` logger.
|
|
||||||
|
|
||||||
.. _how-csrf-works:
|
.. _how-csrf-works:
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
|
@ -328,49 +123,6 @@ vulnerability allows and much worse).
|
||||||
.. _Origin header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
|
.. _Origin header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
|
||||||
.. _disable the referer: https://www.w3.org/TR/referrer-policy/#referrer-policy-delivery
|
.. _disable the referer: https://www.w3.org/TR/referrer-policy/#referrer-policy-delivery
|
||||||
|
|
||||||
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):
|
|
||||||
...
|
|
||||||
|
|
||||||
If you are using class-based views, you can refer to :ref:`Decorating
|
|
||||||
class-based views<decorating-class-based-views>`.
|
|
||||||
|
|
||||||
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:
|
.. _csrf-limitations:
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
|
@ -384,16 +136,10 @@ 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.
|
||||||
|
|
||||||
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
|
Utilities
|
||||||
---------
|
=========
|
||||||
|
|
||||||
|
.. module:: django.views.decorators.csrf
|
||||||
|
|
||||||
The examples below assume you are using function-based views. If you
|
The examples below assume you are using function-based views. If you
|
||||||
are working with class-based views, you can refer to :ref:`Decorating
|
are working with class-based views, you can refer to :ref:`Decorating
|
||||||
|
@ -411,6 +157,21 @@ class-based views<decorating-class-based-views>`.
|
||||||
def my_view(request):
|
def my_view(request):
|
||||||
return HttpResponse('Hello world')
|
return HttpResponse('Hello world')
|
||||||
|
|
||||||
|
.. function:: csrf_protect(view)
|
||||||
|
|
||||||
|
Decorator that provides the protection of ``CsrfViewMiddleware`` to a view.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.views.decorators.csrf import csrf_protect
|
||||||
|
|
||||||
|
@csrf_protect
|
||||||
|
def my_view(request):
|
||||||
|
c = {}
|
||||||
|
# ...
|
||||||
|
return render(request, "a_template.html", c)
|
||||||
|
|
||||||
.. function:: requires_csrf_token(view)
|
.. function:: requires_csrf_token(view)
|
||||||
|
|
||||||
Normally the :ttag:`csrf_token` template tag will not work if
|
Normally the :ttag:`csrf_token` template tag will not work if
|
||||||
|
@ -434,79 +195,6 @@ class-based views<decorating-class-based-views>`.
|
||||||
|
|
||||||
This decorator forces a view to send the CSRF cookie.
|
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 ``CsrfViewMiddleware.process_view`` 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 CSRF 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
|
Settings
|
||||||
========
|
========
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue