Summary

The CSRF middleware and template tag provides easy-to-use protection against CSRF Forgeries.

ref

https://docs.djangoproject.com/en/3.2/ref/csrf/

Protection fc

position ease box interval due
front 2.5 0 0 2021-09-12T07:15:09Z

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. Requests via ‘unsafe’ methods, such as POST, PUT, and DELETE, can then be protected by following the steps below.

Usage

In HTML pages

To take advantage of CSRF protection in your views, follow these steps:

The CSRF middleware is activated by default in the 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 csrf_protect() on particular views you want to protect (see below).

In any template that uses a POST form, use the csrf_token tag inside the <form> element if the form is for an internal URL, e.g.:

<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 RequestContext is used to render the response so that {% csrf_token %} will work properly. If you’re using the render() function, generic views, or contrib apps, you are covered already since these all use RequestContext.

In API using 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 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 CSRF_USE_SESSIONS and CSRF_COOKIE_HTTPONLY settings are enabled.

The recommended source for the token is the csrftoken Cookies, 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 CSRF_COOKIE_NAME setting.

You can acquire the token like this:

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 to replace getCookie:

const csrftoken = Cookies.get('csrftoken');
  • Note

    The CSRF token is also present in the DOM, but only if explicitly included using csrf_token in a template. The cookie contains the canonical token; the CsrfViewMiddleware will prefer the cookie to the token in the DOM. Regardless, you’re guaranteed to have the cookie if the token is present in the DOM, so you should use the cookie!

If you activate CSRF_USE_SESSIONS or CSRF_COOKIE_HTTPONLY, you must include the CSRF token in your HTML and read the token from the DOM with JavaScript:

{% csrf_token %}
<script>
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
</script>
  • CSRF_COOKIE_HTTPONLY

    Default: False

    Whether to use HttpOnly flag on the CSRF cookie. If this is set to True, client-side JavaScript will not be able to access the CSRF cookie.

    Designating the CSRF cookie as HttpOnly doesn’t offer any practical protection because CSRF is only to protect against cross-domain attacks . If an attacker can read the cookie via JavaScript, they’re already on the same domain as far as the browser knows, so they can do anything they like anyway. (XSS is a much bigger hole than CSRF.)

    Although the setting offers little practical benefit, it’s sometimes required by security auditors.

    If you enable this and need to send the value of the CSRF token with an AJAX request, your JavaScript must pull the value from a hidden CSRF token form input instead of from the cookie.

    See SESSION_COOKIE_HTTPONLY for details on HttpOnly.

    • SESSION_COOKIE_HTTPONLY

      Default: True

      Whether to use HttpOnly flag on the session cookie. If this is set to True, client-side JavaScript will not be able to access the session cookie.

      HttpOnly is a flag included in a Set-Cookie HTTP response header. It’s part of the RFC 6265#section-4.1.2.6 standard for cookies and can be a useful way to mitigate the risk of a client-side script accessing the protected cookie data.

      This makes it less trivial for an attacker to escalate a cross-site scripting vulnerability into full hijacking of a user’s session. There aren’t many good reasons for turning this off. Your code shouldn’t read session cookies from JavaScript.

Setting the token on the AJAX request

Finally, you’ll need to set the header on your AJAX request. Using the fetch() API:

const request = new Request(
    /* URL */,
     {headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
    method: 'POST',
    mode: 'same-origin'  // Do not send CSRF token to another domain.
}).then(function(response) {
    // ...
});

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 CSRF_FAILURE_VIEW setting.

How it works

The CSRF protection is based on the following things:

  1. A CSRF cookie that is based on a random secret value, which other sites will not have access to. This cookie is set by CsrfViewMiddleware. It is sent with every response that has called django.middleware.csrf.get_token() (the function used internally to retrieve the CSRF token), if it wasn’t already set on the request. In order to protect against BREACH attacks, the token is not simply the secret; a random mask is prepended to the secret and used to scramble it.

    For security reasons, the value of the secret is changed each time a user logs in.

  2. A hidden form field with the name ‘csrfmiddlewaretoken’ present in all outgoing POST forms. The value of this field is, again, the value of the secret, with a mask which is both added to it and used to scramble it. The mask is regenerated on every call to get_token() so that the form field value is changed in every such response.

    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.

    When validating the ‘csrfmiddlewaretoken’ field value, only the secret, not the full token, is compared with the secret in the cookievalue. This allows the use of ever-changing tokens. While each request may use its own token, the secret remains common to all.

    This check is done by CsrfViewMiddleware.

  4. In addition, for HTTPS requests, strict referer checking is done by CsrfViewMiddleware. This means that even if a subdomain can set or modify cookies on your domain, it can’t force a user to post to your application since that request won’t come from your own exact domain.

    This also addresses a man-in-the-middle attack that’s possible under HTTPS when using a session independent secret, due to the fact that HTTP Set-Cookie headers are (unfortunately) accepted by clients even when they are talking to a site under HTTPS. (Referer checking is not done for HTTP requests because the presence of the Referer header isn’t reliable enough under HTTP.)

    If the CSRF_COOKIE_DOMAIN setting is set, the referer is compared against it. You can allow cross-subdomain requests by including a leading dot. For example, CSRF_COOKIE_DOMAIN = ‘.example.com’ will allow POST requests from www.example.com and api.example.com. If the setting is not set, then the referer must match the HTTP Host header.

    Expanding the accepted referers beyond the current host or cookie domain can be done with the CSRF_TRUSTED_ORIGINS setting.

Example

Implementing CSRF protection in the React with DRF: https://github.com/linkedweb/session_auth.