Подделка межсайтового запроса (CSRF)¶
Промежуточное программное обеспечение CSRF и тег шаблона обеспечивают простую в использовании защиту от подделок межсайтовых запросов. Этот тип атаки происходит, когда вредоносный веб-сайт содержит ссылку, кнопку формы или какой-либо код JavaScript, предназначенный для выполнения определенного действия на вашем веб-сайте с использованием учетных данных вошедшего в систему пользователя, который посещает вредоносный сайт в своем браузере. Также рассматривается родственный тип атаки, «login CSRF», когда атакующий сайт обманом заставляет браузер пользователя войти на сайт с чужим учетными данными.
Первая защита от атак CSRF — гарантировать, что запросы GET (и другие «безопасные» методы, как определено в RFC 9110 Section 9.2.1) не имеют побочных эффектов. Запросы с помощью «небезопасных» методов, таких как POST, PUT и DELETE, затем можно защитить с помощью шагов, описанных в Как пользоваться системой защиты от CSRF в Django.
Как это работает¶
CSRF базируется на следующих вещах:
Файл cookie CSRF, представляющий собой случайное секретное значение, к которому другие сайты не будут иметь доступа.
CsrfViewMiddlewareотправляет этот файл cookie вместе с ответом всякий раз, когда вызываетсяdjango.middleware.csrf.get_token(). Он также может отправить его в других случаях. По соображениям безопасности значение секрета меняется каждый раз, когда пользователь входит в систему.Скрытое поле формы с именем «csrfmiddlewaretoken», присутствующее во всех исходящих формах POST.
Чтобы защититься от атак BREACH, значение этого поля не является просто секретом. Он шифруется по-разному для каждого ответа с использованием маски. Маска генерируется случайным образом при каждом вызове get_token(), поэтому значение поля формы каждый раз отличается.
Эту часть выполняет тег шаблона
csrf_token.Все HTTP запросы, которые не GET, HEAD, OPTIONS или TRACE, должны содержать CSRF куку, и поле „csrfmiddlewaretoken“ с правильным значением. Иначе пользователь получит 403 ошибку.
При проверке значения поля csrfmiddlewaretoken сравнивается только секрет, а не полный токен, с секретом в значении файла cookie. Это позволяет использовать постоянно меняющиеся токены. Хотя каждый запрос может использовать свой собственный токен, секрет остается общим для всех.
Эта проверка выполняется в
CsrfViewMiddleware.CsrfViewMiddlewareпроверяет ``Заголовок Origin`_, если он предоставлен браузером, на соответствие текущему хосту и настройкеCSRF_TRUSTED_ORIGINS. Это обеспечивает защиту от междоменных атак.Кроме того, для запросов HTTPS, если заголовок Origin не указан, CsrfViewMiddleware выполняет строгую проверку реферера. Это означает, что даже если субдомен может устанавливать или изменять файлы cookie в вашем домене, он не может заставить пользователя публиковать сообщения в вашем приложении, поскольку этот запрос не будет исходить из вашего собственного домена.
В дополнение для HTTPS запросов в
CsrfViewMiddlewareпроверяется «referer»(источник запроса). Это необходимо для предотвращения MITM-атаки(Man-In-The-Middle), которая возможна при использовании HTTPS и токена не привязанного к сессии, т.к. клиенты принимают(к сожалению) HTTP заголовок „Set-Cookie“, несмотря на то, что коммуникация с сервером происходит через HTTPS. (Такая проверка не выполняется для HTTP запросов т.к. «Referer» заголовок легко подменить при использовании HTTP.)Если установлен параметр
CSRF_COOKIE_DOMAIN, референт сравнивается с ним. Вы можете разрешить запросы между поддоменами, включив точку в начале. Например, CSRF_COOKIE_DOMAIN = .example.com разрешит запросы POST от www.example.com и api.example.com. Если параметр не установлен, то реферер должен соответствовать заголовку HTTPHost.Чтобы расширить список доступных доменов, кроме текущего хоста и домена кук, используйте
CSRF_TRUSTED_ORIGINS.
Такой подход гарантирует, что только формы, отправленные с доверенных доменов, могут передавать POST данные.
Он намеренно игнорирует запросы GET (и другие запросы, которые определены как «безопасные» в RFC 9110 Section 9.2.1). Эти запросы никогда не должны иметь потенциально опасных побочных эффектов, поэтому CSRF-атака с запросом GET должна быть безвредной. RFC 9110 Section 9.2.1 определяет POST, PUT и DELETE как «небезопасные», а все остальные методы также считаются небезопасными для максимальной защиты.
Защита CSRF не может защитить от атак «человек посередине», поэтому используйте HTTPS с HTTP Strict Transport Security. Он также предполагает проверку заголовка HOST и отсутствие уязвимостей межсайтового скриптинга на вашем сайте (поскольку XSS-уязвимости уже позволяют злоумышленнику делать все, что позволяет уязвимость CSRF, и даже хуже).
Удаление заголовка «Referer»
Чтобы избежать раскрытия URL-адреса реферера сторонним сайтам, вы можете захотеть отключить реферер`_ в тегах ``<a> вашего сайта. Например, вы можете использовать тег <meta name="referrer" content="no-referrer"> или включить заголовок Referrer-Policy: no-referrer. Из-за строгой проверки ссылок защиты CSRF на запросах HTTPS эти методы вызывают сбой CSRF на запросах с «небезопасными» методами. Вместо этого используйте альтернативы, такие как <a rel="noreferrer" ...>" для ссылок на сторонние сайты.
Ограничения¶
Субдомены на сайте смогут устанавливать файлы cookie на клиенте для всего домена. Установив файл cookie и используя соответствующий токен, субдомены смогут обойти защиту CSRF. Единственный способ избежать этого — убедиться, что субдомены контролируются доверенными пользователями (или, по крайней мере, не могут устанавливать файлы cookie). Обратите внимание, что даже без CSRF существуют другие уязвимости, такие как фиксация сеанса, из-за которых передача поддоменов ненадежным сторонам является плохой идеей, и эти уязвимости невозможно легко исправить с помощью текущих браузеров.
Утилиты¶
Примеры ниже предполагают, чтобы используете представления-функции. Если вы используете представления-классы, обратитесь к этому разделу.
- csrf_exempt(view)¶
Этот декоратор позволяет исключить представление из процесса проверки CSRF. Например:
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 куку.
Настройки¶
Параметры, которые могут быть использованы для управления поведением CSRF в Django:
Часто задаваемые вопросы¶
Проблема в том, что CSRF-защита Django по умолчанию не связана с сеансом?¶
Нет, это задумано. Отсутствие привязки защиты CSRF к сеансу позволяет использовать защиту на таких сайтах, как pastebin, которые позволяют отправлять сообщения анонимным пользователям, у которых нет сеанса.
Если вы хотите сохранить токен CSRF в сеансе пользователя, используйте настройку CSRF_USE_SESSIONS.
Почему пользователь может столкнуться с ошибкой проверки CSRF после входа в систему?¶
По соображениям безопасности токены CSRF меняются каждый раз, когда пользователь входит в систему. Любая страница с формой, созданной до входа в систему, будет иметь старый, недействительный токен CSRF, и ее необходимо будет перезагрузить. Это может произойти, если пользователь использует кнопку «Назад» после входа в систему или входит в другую вкладку браузера.