Защита от подделки межсайтовых запросов

The CSRF middleware and template tag provides easy-to-use protection against Cross Site Request Forgeries. This type of attack occurs when a malicious website contains a link, a form button or some JavaScript that is intended to perform some action on your website, 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 RFC 9110#section-9.2.1) are side effect free. Requests via „unsafe“ methods, such as POST, PUT, and DELETE, can then be protected by the steps outlined in Как использовать защиту от CSRF в Django.

Как это работает

Защита от CSRF основана на следующих моментах:

  1. A CSRF cookie that is a random secret value, which other sites will not have access to.

    CsrfViewMiddleware отправляет этот cookie вместе с ответом всякий раз, когда вызывается django.middleware.csrf.get_token(). Он также может отправлять его в других случаях. В целях безопасности значение секрета изменяется каждый раз, когда пользователь входит в систему.

  2. Скрытое поле формы с именем „csrfmiddlewaretoken“, присутствующее во всех исходящих POST-формах.

    Для защиты от атак BREACH значение этого поля не является простым секретом. Оно скремблируется по-разному при каждом ответе с помощью маски. Маска генерируется случайным образом при каждом вызове get_token(), поэтому значение поля формы каждый раз разное.

    Эту часть выполняет тег шаблона.

  3. Для всех входящих запросов, не использующих HTTP GET, HEAD, OPTIONS или TRACE, должен присутствовать CSRF cookie, а поле „csrfmiddlewaretoken“ должно быть настоящим и правильным. Если это не так, пользователь получит ошибку 403.

    При проверке значения поля „csrfmiddlewaretoken“ только секрет, а не полный токен, сравнивается с секретом в значении cookie. Это позволяет использовать постоянно меняющиеся маркеры. Хотя каждый запрос может использовать свой собственный токен, секрет остается общим для всех.

    Эта проверка выполняется с помощью CsrfViewMiddleware.

  4. CsrfViewMiddleware проверяет Origin header, если он предоставлен браузером, на соответствие текущему хосту и настройке CSRF_TRUSTED_ORIGINS. Это обеспечивает защиту от кросс-субдоменных атак.

  5. In addition, for HTTPS requests, if the Origin header isn’t provided, CsrfViewMiddleware performs strict referer checking. 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.

    Это также устраняет атаку «человек посередине», которая возможна в HTTPS при использовании независимого от сессии секрета, из-за того, что заголовки HTTP Set-Cookie (к сожалению) принимаются клиентами, даже когда они разговаривают с сайтом в HTTPS. (Проверка ссылок не выполняется для HTTP-запросов, потому что наличие заголовка Referer не является достаточно надежным в 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.

    Расширить список принимаемых ссылок за пределы текущего хоста или домена cookie можно с помощью параметра CSRF_TRUSTED_ORIGINS.

Changed in Django Development version:

В старых версиях значение CSRF cookie было замаскировано.

Это гарантирует, что только формы, полученные из доверенных доменов, могут быть использованы для POST данных.

It deliberately ignores GET requests (and other requests that are defined as „safe“ by RFC 9110#section-9.2.1). 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 9110#section-9.2.1 defines POST, PUT, and DELETE as „unsafe“, and all other methods are also assumed to be unsafe, for maximum protection.

Защита CSRF не может защитить от атак типа «человек посередине», поэтому используйте HTTPS с Строгая транспортная безопасность HTTP. Это также предполагает validation of the HOST header и отсутствие cross-site scripting vulnerabilities на вашем сайте (потому что XSS-уязвимости уже позволяют злоумышленнику делать все, что позволяет CSRF-уязвимость, и даже намного хуже).

Удаление заголовка Referer

Чтобы избежать раскрытия URL-адреса реферера сторонним сайтам, вы можете использовать теги disable the referer на вашем сайте <a>. Например, вы можете использовать тег <meta name="referrer" content="no-referrer"> или включить заголовок Referrer-Policy: no-referrer. Из-за того, что защита CSRF строго проверяет ссылки на запросы HTTPS, эти техники вызывают отказ CSRF на запросы с «небезопасными» методами. Вместо этого используйте альтернативы, такие как <a rel="noreferrer" ...>" для ссылок на сторонние сайты.

Ограничения

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.

Утилиты

Приведенные ниже примеры предполагают, что вы используете представления на основе функций. Если вы работаете с представлениями на основе классов, вы можете обратиться к Decorating class-based views.

csrf_exempt(view)[исходный код]

Этот декоратор помечает представление как освобожденное от защиты, обеспечиваемой промежуточным ПО. Пример:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')
csrf_protect(view)

Декоратор, обеспечивающий защиту CsrfViewMiddleware для представления.

Использование:

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)
requires_csrf_token(view)

Обычно тег шаблона csrf_token не работает, если не запущен тег CsrfViewMiddleware.process_view или эквивалентный ему csrf_protect. Декоратор представления requires_csrf_token может быть использован для обеспечения работы тега шаблона. Этот декоратор работает аналогично csrf_protect, но никогда не отклоняет входящий запрос.

Пример:

from django.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

Этот декоратор заставляет представление отправлять CSRF cookie.

Настройки

Для управления поведением Django в отношении CSRF можно использовать ряд настроек:

Часто задаваемые вопросы

Является ли размещение произвольной пары CSRF-токенов (cookie и POST-данные) уязвимостью?

Нет, это сделано специально. Без атаки «человек посередине» у злоумышленника нет возможности отправить CSRF маркер cookie в браузер жертвы, поэтому для успешной атаки необходимо получить cookie браузера жертвы через XSS или подобным образом, в этом случае злоумышленнику обычно не нужны CSRF атаки.

Некоторые инструменты аудита безопасности отмечают это как проблему, но, как уже говорилось, злоумышленник не может украсть CSRF-куки браузера пользователя. «Кража» или изменение собственного маркера с помощью Firebug, инструментов разработчика Chrome и т.д. не является уязвимостью.

Является ли проблемой то, что защита CSRF в Django по умолчанию не связана с сессией?

Нет, это сделано специально. Отсутствие привязки CSRF-защиты к сессии позволяет использовать защиту на таких сайтах, как pastebin, которые позволяют отправлять сообщения от анонимных пользователей, не имеющих сессии.

Если вы хотите хранить CSRF-токен в сессии пользователя, используйте параметр CSRF_USE_SESSIONS.

Почему пользователь может столкнуться с ошибкой проверки CSRF после входа в систему?

В целях безопасности маркеры CSRF меняются каждый раз, когда пользователь входит в систему. Любая страница с формой, созданной до входа в систему, будет иметь старый, недействительный CSRF-токен, и ее необходимо будет перезагрузить. Это может произойти, если пользователь использует кнопку «назад» после входа в систему или если он входит в другую вкладку браузера.

Вернуться на верх