Как использовать защиту Django от CSRF
Чтобы использовать защиту от CSRF в своих представлениях, выполните следующие действия:
-
По умолчанию промежуточное ПО CSRF включено в настройке
MIDDLEWARE. Если вы переопределяете эту настройку, помните, что'django.middleware.csrf.CsrfViewMiddleware'должно располагаться перед любым промежуточным ПО представлений, которое предполагает, что атаки CSRF уже обработаны.Если вы отключили его, что не рекомендуется, можно использовать
csrf_protect()для отдельных представлений, которые нужно защитить (см. ниже). -
В любом шаблоне, содержащем форму POST, используйте тег
csrf_tokenвнутри элемента<form>, если форма отправляется на внутренний URL-адрес. Например:<form method="post">{% csrf_token %}Не следует делать этого для форм POST, отправляемых на внешние URL-адреса, поскольку это приведёт к утечке токена CSRF и создаст уязвимость.
- В соответствующих функциях представлений убедитесь, что для отображения ответа используется
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.
Установка токена в 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/