Spec-Zone.ru › Django 4.2

Как использовать защиту 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');

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

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’s Jinja2 шаблонный движок добавляет {{ csrf_input }} в контекст всех шаблонов, что эквивалентно {% csrf_token %} в языке шаблонов Django. Например:

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

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

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

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

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

По умолчанию, если входящий запрос не пройдёт проверки, выполняемые CsrfViewMiddleware, пользователю отправляется ответ с кодом состояния «403 Запрещено». Это обычно наблюдается только при реальной атаке типа «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, так что они больше не отклоняют запросы. Во всех других отношениях (например, отправка куки и т. д.) они ведут себя одинаково.

Если по какой-то причине вы хотите, чтобы тестовый клиент выполнял проверки CSRF, вы можете создать экземпляр тестового клиента, который обеспечивает проверки 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, которая бы привела к отправке необходимого куки 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/4.2/howto/csrf/

Spec-Zone.ru

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