Spec-Zone.ru › Django 6.0

Как использовать защиту Django от CSRF

Чтобы использовать защиту от CSRF в своих представлениях, выполните следующие действия:

  1. По умолчанию промежуточное ПО CSRF включено в настройке MIDDLEWARE. Если вы переопределяете эту настройку, помните, что 'django.middleware.csrf.CsrfViewMiddleware' должно располагаться перед любым промежуточным ПО представлений, которое предполагает, что атаки CSRF уже обработаны.

    Если вы отключили его, что не рекомендуется, можно использовать csrf_protect() для отдельных представлений, которые нужно защитить (см. ниже).

  2. В любом шаблоне, содержащем форму POST, используйте тег csrf_token внутри элемента <form>, если форма отправляется на внутренний URL-адрес. Например:

    <form method="post">{% csrf_token %}
    

    Не следует делать этого для форм POST, отправляемых на внешние URL-адреса, поскольку это приведёт к утечке токена CSRF и создаст уязвимость.

  3. В соответствующих функциях представлений убедитесь, что для отображения ответа используется RequestContext, чтобы {% csrf_token %} работал правильно. Если вы используете функцию render(), универсальные представления или приложения contrib, всё уже настроено: все они используют RequestContext.

Защита от CSRF при использовании AJAX

Описанный выше метод можно использовать для AJAX-запросов POST, но он имеет некоторые неудобства: нужно не забывать передавать токен CSRF в данных POST для каждого запроса POST. Поэтому существует альтернативный метод: для каждого XMLHttpRequest устанавливайте пользовательский заголовок X-CSRFToken (как указано в настройке CSRF_HEADER_NAME) со значением токена CSRF. Часто это проще, поскольку многие JavaScript-фреймворки предоставляют хуки, позволяющие устанавливать заголовки для каждого запроса.

Сначала необходимо получить токен CSRF. Способ зависит от того, включены ли настройки CSRF_USE_SESSIONS и CSRF_COOKIE_HTTPONLY.

Получение токена, если CSRF_USE_SESSIONS и CSRF_COOKIE_HTTPONLY имеют значение False

Рекомендуемый источник токена — cookie csrftoken, которая будет установлена, если вы включили защиту от CSRF для своих представлений, как описано выше.

По умолчанию cookie с токеном CSRF называется csrftoken, но имя cookie можно изменить с помощью настройки CSRF_COOKIE_NAME.

Получить токен можно следующим образом:

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');

Приведённый выше код можно упростить, заменив getCookie с помощью библиотеки JavaScript Cookie:

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

Примечание

Токен CSRF также присутствует в DOM в маскированном виде, но только если он явно включён в шаблон с помощью csrf_token. Cookie содержит канонический, немаскированный токен. CsrfViewMiddleware принимает оба варианта. Однако для защиты от атак BREACH рекомендуется использовать маскированный токен.

Предупреждение

Если ваше представление не отображает шаблон, содержащий тег шаблона csrf_token, Django может не установить cookie с токеном CSRF. Это часто встречается, когда формы добавляются на страницу динамически. Для решения этой проблемы Django предоставляет декоратор представления, который принудительно устанавливает cookie: ensure_csrf_cookie().

Получение токена, если CSRF_USE_SESSIONS или CSRF_COOKIE_HTTPONLY имеет значение True

Если вы включили CSRF_USE_SESSIONS или CSRF_COOKIE_HTTPONLY, необходимо включить токен CSRF в HTML и прочитать его из DOM с помощью JavaScript:

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

Установка токена в AJAX-запросе

Наконец, необходимо установить заголовок в AJAX-запросе. С помощью API fetch():

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) {
    // ...
});

Использование защиты от CSRF в шаблонах Jinja2

Бэкенд шаблонов Django Jinja2 добавляет {{ csrf_input }} в контекст всех шаблонов. Это эквивалентно {% csrf_token %} в языке шаблонов Django. Например:

<form method="post">{{ csrf_input }}

Использование декоратора

Вместо того чтобы повсеместно добавлять CsrfViewMiddleware для защиты, можно использовать декоратор csrf_protect(), обладающий точно такой же функциональностью, для отдельных представлений, которым нужна защита. Его необходимо применять как к представлениям, добавляющим токен CSRF в вывод, так и к тем, которые принимают данные формы POST. (Часто это одна и та же функция представления, но не всегда.)

Использовать только декоратор не рекомендуется, поскольку, если вы забудете его применить, возникнет уязвимость. Стратегия «ремень и подтяжки» с использованием обоих вариантов вполне подходит и создаёт минимальные дополнительные затраты.

Обработка отклонённых запросов

По умолчанию, если входящий запрос не проходит проверки, выполняемые CsrfViewMiddleware, пользователю отправляется ответ «403 Forbidden». Обычно это происходит только при настоящей атаке Cross Site Request Forgery или если из-за ошибки программирования токен CSRF не был включён в форму POST.

Однако страница ошибки не слишком дружелюбна, поэтому вы можете создать собственное представление для обработки этой ситуации. Для этого задайте настройку CSRF_FAILURE_VIEW.

Сбои проверки CSRF регистрируются как предупреждения в журнале django.security.csrf.

Использование защиты от CSRF с кэшированием

Если шаблон использует тег шаблона csrf_token (или функция get_token вызывается другим способом), CsrfViewMiddleware добавит в ответ cookie и заголовок Vary: Cookie. Это означает, что промежуточное ПО будет корректно работать с промежуточным ПО кэширования, если оно используется согласно инструкциям (UpdateCacheMiddleware должно располагаться перед всем остальным промежуточным ПО).

Однако если вы используете декораторы кэширования для отдельных представлений, промежуточное ПО CSRF ещё не успеет установить заголовок Vary или cookie CSRF, и ответ будет кэширован без них. В этом случае для всех представлений, в которые необходимо вставить токен CSRF, сначала следует использовать декоратор django.views.decorators.csrf.csrf_protect():

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): ...

Если вы используете представления на основе классов, обратитесь к разделу Декорирование представлений на основе классов.

Тестирование и защита от CSRF

CsrfViewMiddleware обычно существенно затрудняет тестирование функций представлений из-за необходимости передавать токен CSRF с каждым запросом POST. Поэтому HTTP-клиент Django для тестов был изменён: он устанавливает для запросов флаг, который ослабляет проверки промежуточного ПО и декоратора csrf_protect, так что они больше не отклоняют запросы. Во всех остальных отношениях (например, при отправке cookie) они работают так же.

Если по какой-либо причине вы хотите, чтобы тестовый клиент выполнял проверки CSRF, можно создать экземпляр тестового клиента, который принудительно выполняет эти проверки:

>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)

Особые случаи

Некоторые представления могут иметь необычные требования, из-за которых они не соответствуют описанному здесь стандартному шаблону. В таких ситуациях могут быть полезны различные средства. Сценарии их применения описаны в следующем разделе.

Отключение защиты от CSRF только для нескольких представлений

Большинству представлений нужна защита от CSRF, но некоторым — нет.

Решение: вместо отключения промежуточного ПО и применения csrf_protect ко всем представлениям, которым нужна защита, включите промежуточное ПО и используйте csrf_exempt().

Установка токена, если не используется CsrfViewMiddleware.process_view()

Иногда CsrfViewMiddleware.process_view может не выполниться до вызова вашего представления — например, в обработчиках ошибок 404 и 500, — но токен CSRF всё равно нужен в форме.

Решение: используйте requires_csrf_token().

Добавление токена CSRF в незащищённое представление

Некоторые представления могут быть незащищёнными и исключёнными с помощью csrf_exempt, но при этом им всё равно нужно включать токен CSRF.

Решение: используйте csrf_exempt(), а затем requires_csrf_token() (то есть requires_csrf_token должен быть самым внутренним декоратором).

Защита представления только для одного пути

Представлению нужна защита от CSRF только при одном наборе условий, а в остальное время она не должна применяться.

Решение: примените csrf_exempt() ко всей функции представления, а csrf_protect() — к пути внутри неё, который нужно защитить. Пример:

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

Защита страницы, использующей AJAX без HTML-формы

Страница отправляет запрос POST через AJAX, но на ней нет HTML-формы с тегом csrf_token, который обеспечил бы отправку необходимой cookie CSRF.

Решение: используйте ensure_csrf_cookie() для представления, которое отправляет страницу.

Защита от CSRF в повторно используемых приложениях

Поскольку разработчик может отключить CsrfViewMiddleware, все соответствующие представления в приложениях contrib используют декоратор csrf_protect, чтобы защитить эти приложения от CSRF. Разработчикам других повторно используемых приложений, которым нужны такие же гарантии, рекомендуется также использовать декоратор csrf_protect для своих представлений.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/howto/csrf/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API