Как использовать защиту Django от CSRF
Чтобы использовать защиту от CSRF в ваших представлениях, следуйте этим шагам:
-
Средство промежуточного программного обеспечения CSRF активировано по умолчанию в настройке
MIDDLEWARE. Если вы переопределяете эту настройку, помните, что'django.middleware.csrf.CsrfViewMiddleware'должно стоять перед любым средством промежуточного программного обеспечения представлений, которое предполагает, что атаки CSRF были предотвращены.Если вы его отключили, что не рекомендуется, вы можете использовать
csrf_protect()для отдельных представлений, которые вы хотите защитить (см. ниже). -
В любом шаблоне, использующем форму POST, используйте тег
csrf_tokenвнутри элемента<form>, если форма предназначена для внутренней ссылки, например:<form method="post">{% csrf_token %}Это не следует делать для форм POST, которые обращаются к внешним URL-адресам, так как это может привести к утечке токена CSRF, что создаст уязвимость.
- В соответствующих функциях представлений убедитесь, что используется
RequestContextдля рендеринга ответа, чтобы{% csrf_token %}работало правильно. Если вы используете функциюrender(), обобщенные представления или приложения contrib, вы уже защищены, так как все они используютRequestContext.
Использование защиты CSRF с AJAX
Хотя вышеуказанный метод можно использовать для запросов POST с AJAX, он имеет некоторые неудобства: вам нужно помнить о необходимости передачи токена 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 токена 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_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
Обработка шаблонов Jinja2 Django Jinja2 добавляет {{ csrf_input }} в контекст всех шаблонов, что эквивалентно {% csrf_token %} в языке шаблонов Django. Например:
<form method="post">{{ csrf_input }}
Использование метода декоратора
Вместо добавления CsrfViewMiddleware в качестве общей защиты, вы можете использовать декоратор csrf_protect(), который имеет точно такую же функциональность, для отдельных представлений, которым нужна защита. Он должен использоваться как для представлений, которые вставляют токен CSRF в вывод, так и для представлений, которые принимают данные формы POST. (Это часто одна и та же функция представления, но не всегда).
Использование только декоратора не рекомендуется, так как если вы забудете его использовать, у вас будет брешь в безопасности. Стратегия «страховки» использования обоих вариантов приемлема и повлечет минимальную нагрузку.
Обработка отклоненных запросов
По умолчанию пользователю отправляется ответ «403 Запрещено», если входящий запрос не проходит проверки, выполненные CsrfViewMiddleware. Это обычно наблюдается только при реальной атаке 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, вы можете создать экземпляр тестового клиента, который принудительно выполняет проверки 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/5.1/howto/csrf/