Защита от межсайтовой подделки запросов
Промежуточное ПО CSRF и тег шаблона обеспечивают простую в использовании защиту от межсайтовой подделки запросов. Этот тип атаки происходит, когда вредоносный веб-сайт содержит ссылку, кнопку формы или JavaScript-код, предназначенные для выполнения каких-либо действий на вашем веб-сайте с использованием учётных данных вошедшего в систему пользователя, посетившего вредоносный сайт в браузере. Также рассматривается связанный тип атаки — «CSRF при входе в систему», при котором атакующий сайт обманом заставляет браузер пользователя войти на сайт с учётными данными другого человека.
Первая мера защиты от CSRF-атак — убедиться, что GET-запросы (и другие «безопасные» методы, как определено в разделе 9.2.1 RFC 9110) не вызывают побочных эффектов. Затем запросы с использованием «небезопасных» методов, таких как POST, PUT и DELETE, можно защитить с помощью действий, описанных в разделе Как использовать защиту Django от CSRF.
Как работает защита от CSRF
Защита от CSRF основана на следующем:
-
CSRF-cookie — это случайное секретное значение, к которому другие сайты не имеют доступа.
CsrfViewMiddlewareотправляет эту cookie в ответе при каждом вызовеdjango.middleware.csrf.get_token(). Она может отправляться и в других случаях. В целях безопасности значение секрета меняется каждый раз, когда пользователь входит в систему. -
Скрытое поле формы с именем ‘csrfmiddlewaretoken’, присутствующее во всех исходящих POST-формах.
Для защиты от атак BREACH значение этого поля не является самим секретом. При каждом ответе оно по-разному перемешивается с помощью маски. Маска генерируется случайным образом при каждом вызове
get_token(), поэтому значение поля формы каждый раз отличается.Эту часть обеспечивает тег шаблона
csrf_token. -
Для всех входящих запросов, использующих HTTP-метод, отличный от GET, HEAD, OPTIONS или TRACE, должна присутствовать CSRF-cookie, а поле ‘csrfmiddlewaretoken’ должно присутствовать и содержать правильное значение. В противном случае пользователь получит ошибку 403.
При проверке значения поля ‘csrfmiddlewaretoken’ с секретом в значении cookie сравнивается только секрет, а не весь токен. Это позволяет использовать постоянно меняющиеся токены. Хотя для каждого запроса может использоваться свой токен, секрет остаётся общим для всех.
Эту проверку выполняет
CsrfViewMiddleware. -
CsrfViewMiddlewareпроверяет заголовок Origin, если браузер его передал, сверяя его с текущим хостом и настройкойCSRF_TRUSTED_ORIGINS. Это обеспечивает защиту от атак с межсайтовых поддоменов. -
Кроме того, для HTTPS-запросов, если заголовок
Originне передан,CsrfViewMiddlewareвыполняет строгую проверку referer. Это означает, что даже если поддомен может устанавливать или изменять cookie для вашего домена, он не сможет заставить пользователя отправить запрос в ваше приложение, поскольку запрос будет поступать не с вашего точного домена.Это также устраняет атаку «человек посередине», возможную при использовании HTTPS с секретом, не связанным с сеансом, поскольку HTTP-заголовки
Set-Cookie(к сожалению) принимаются клиентами, даже когда они обращаются к сайту по HTTPS. (Для HTTP-запросов проверка referer не выполняется, поскольку наличие заголовкаRefererпри использовании HTTP недостаточно надёжно.)Если задана настройка
CSRF_COOKIE_DOMAIN, referer сравнивается с её значением. Разрешить запросы с поддоменов можно, добавив точку в начале. Например,CSRF_COOKIE_DOMAIN = '.example.com'разрешит POST-запросы сwww.example.comиapi.example.com. Если настройка не задана, referer должен совпадать с HTTP-заголовкомHost.Расширить список допустимых referer за пределы текущего хоста или домена cookie можно с помощью настройки
CSRF_TRUSTED_ORIGINS.
Это гарантирует, что для отправки данных методом POST можно использовать только формы, созданные на доверенных доменах.
Проверка намеренно игнорирует GET-запросы (и другие запросы, определённые как «безопасные» в разделе 9.2.1 RFC 9110). Такие запросы никогда не должны вызывать потенциально опасных побочных эффектов, поэтому CSRF-атака с помощью GET-запроса должна быть безвредной. В разделе 9.2.1 RFC 9110 методы POST, PUT и DELETE определены как «небезопасные»; для максимальной защиты все остальные методы также считаются небезопасными.
Защита от CSRF не защищает от атак «человек посередине», поэтому используйте HTTPS вместе с HTTP Strict Transport Security. Также предполагается, что выполняется проверка заголовка HOST и на вашем сайте нет уязвимостей межсайтового скриптинга (поскольку такие уязвимости и без того позволяют атакующему делать всё, что возможно при уязвимости CSRF, и даже больше).
Удаление заголовка Referer
Чтобы не раскрывать URL-адрес источника сторонним сайтам, можно отключить referer для тегов <a> на своём сайте. Например, можно использовать тег <meta name="referrer" content="no-referrer"> или включить заголовок Referrer-Policy: no-referrer. Из-за строгой проверки referer при HTTPS-запросах в рамках защиты от CSRF эти способы приведут к ошибке CSRF для запросов с «небезопасными» методами. Вместо этого используйте альтернативы, например <a rel="noreferrer" ...>", для ссылок на сторонние сайты.
Ограничения
Поддомены сайта могут устанавливать на клиенте cookie для всего домена. Установив cookie и используя соответствующий токен, поддомены смогут обойти защиту от CSRF. Единственный способ избежать этого — убедиться, что поддомены контролируются доверенными пользователями (или, по крайней мере, не могут устанавливать cookie). Обратите внимание: даже без CSRF существуют другие уязвимости, например фиксация сессии, поэтому предоставлять поддомены недоверенным лицам — плохая идея. Эти уязвимости нелегко устранить средствами современных браузеров.
Вспомогательные средства
В приведённых ниже примерах предполагается, что вы используете представления на основе функций. Если вы работаете с представлениями на основе классов, обратитесь к разделу Декорирование представлений на основе классов.
-
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)
-
ensure_csrf_cookie(view) -
Этот декоратор заставляет представление отправлять CSRF-cookie.
Настройки
Для управления поведением Django в отношении CSRF можно использовать несколько настроек:
Часто задаваемые вопросы
Проблема ли то, что защита Django от CSRF по умолчанию не связана с сеансом?
Нет, так задумано. Отсутствие привязки защиты от CSRF к сеансу позволяет использовать её на таких сайтах, как pastebin, которые принимают сообщения от анонимных пользователей без сеанса.
Если вы хотите хранить CSRF-токен в сеансе пользователя, используйте настройку CSRF_USE_SESSIONS.
Почему после входа в систему пользователь может столкнуться с ошибкой проверки CSRF?
В целях безопасности CSRF-токены меняются каждый раз, когда пользователь входит в систему. На любой странице с формой, созданной до входа в систему, будет старый недействительный CSRF-токен, поэтому такую страницу нужно перезагрузить. Это может произойти, если пользователь нажмёт кнопку «Назад» после входа или войдёт в систему в другой вкладке браузера.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/ref/csrf/