Защита от межсайтовых поддельных запросов
Средства CSRF-защиты и тег шаблона обеспечивают простую в использовании защиту от межсайтовых поддельных запросов. Этот тип атаки происходит, когда вредоносный веб-сайт содержит ссылку, кнопку формы или JavaScript-код, предназначенный для выполнения действия на вашем веб-сайте с использованием учетных данных вошедшего в систему пользователя, который посещает вредоносный сайт в своём браузере. Также обрабатывается связанный тип атаки, «CSRF-атака входа», где атакующий сайт обманывает браузер пользователя, заставляя его войти на сайт под чужими учетными данными.
Первой защитой от CSRF-атак является обеспечение того, чтобы запросы GET (и другие «безопасные» методы, как определено в RFC 9110#section-9.2.1) не имели побочных эффектов. Запросы с использованием «небезопасных» методов, таких как POST, PUT и DELETE, могут быть защищены шагами, описанными в Как использовать защиту Django от CSRF.
Как это работает
Защита от CSRF основана на следующем:
-
CSRF-куки, содержащий случайное секретное значение, к которому другие сайты не имеют доступа.
CsrfViewMiddlewareотправляет эти куки с ответом всякий раз, когдаdjango.middleware.csrf.get_token()вызывается. Он также может отправлять их в других случаях. По соображениям безопасности значение секрета изменяется каждый раз при входе пользователя. -
Скрытое поле формы с именем ‘csrfmiddlewaretoken’, присутствующее во всех исходящих формах POST.
Для защиты от атак BREACH значение этого поля не просто секрет. Оно зашифровывается по-разному с каждым ответом с использованием маски. Маска генерируется случайным образом при каждом вызове
get_token(), поэтому значение поля формы отличается каждый раз.Эта часть выполняется тегом шаблона.
-
Для всех входящих запросов, не использующих HTTP GET, HEAD, OPTIONS или TRACE, должна быть присутствующей и верной CSRF-куки и поле ‘csrfmiddlewaretoken’. Если этого нет, пользователю будет выдано сообщение об ошибке 403.
При проверке значения поля ‘csrfmiddlewaretoken’ сравнивается только секрет, а не весь токен, с секретом в значении куки. Это позволяет использовать постоянно изменяющиеся токены. Хотя каждый запрос может использовать свой собственный токен, секрет остается общим для всех.
Эта проверка выполняется
CsrfViewMiddleware. -
CsrfViewMiddlewareпроверяет заголовок Origin, если он предоставлен браузером, по отношению к текущему хосту и настройкеCSRF_TRUSTED_ORIGINS. Это обеспечивает защиту от атак через разные поддомены. -
Кроме того, для запросов HTTPS, если заголовок
Originне предоставлен,CsrfViewMiddlewareвыполняет строгую проверку referer. Это означает, что даже если поддомен может устанавливать или изменять куки на вашем домене, он не может заставить пользователя отправить запрос на ваше приложение, так как этот запрос не будет поступать с вашего точного домена.Это также решает проблему атаки «человек посередине», которая возможна под HTTPS при использовании независимого от сессии секрета, из-за того, что HTTP-заголовки
Set-Cookie(к сожалению) принимаются клиентами даже когда они взаимодействуют с сайтом под HTTPS. (Проверка referer не выполняется для HTTP-запросов, потому что наличие заголовкаRefererнедостаточно надёжно в HTTP-среде.)Если настройка
CSRF_COOKIE_DOMAINустановлена, referer сравнивается с ней. Вы можете разрешить запросы с разных поддоменов, включив ведущую точку. Например,CSRF_COOKIE_DOMAIN = '.example.com'позволит запросы POST сwww.example.comиapi.example.com. Если настройка не установлена, то referer должен соответствовать заголовку HTTPHost.Расширение разрешённых referer за пределы текущего хоста или домена кук можно сделать с помощью настройки
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 на вашем сайте (потому что XSS-уязвимости уже позволяют злоумышленнику сделать всё, что позволяет CSRF-уязвимость, и гораздо больше).
Удаление заголовка Referer
Чтобы избежать раскрытия URL-адреса referer сторонним сайтам, вы можете отключить referer на тегах <a> вашего сайта. Например, вы можете использовать тег <meta name="referrer" content="no-referrer"> или включить заголовок Referrer-Policy: no-referrer. Из-за строгой проверки referer в CSRF-защите для запросов HTTPS эти методы приводят к ошибке CSRF для запросов с «небезопасными» методами. Вместо этого используйте альтернативы, такие как <a rel="noreferrer" ...>" для ссылок на сторонние сайты.
Ограничения
Поддомены на одном сайте смогут устанавливать куки для всего домена. Установив куки и используя соответствующий токен, поддомены смогут обойти защиту от CSRF. Единственный способ избежать этого — убедиться, что поддомены контролируются доверенными пользователями (или, по крайней мере, не могут устанавливать куки). Обратите внимание, что даже без 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")Изменено в Django 5.0:Добавлена поддержка обертывания асинхронных функций представлений.
-
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)Изменено в Django 5.0:Добавлена поддержка обертывания асинхронных функций представлений.
-
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)Изменено в Django 5.0:Добавлена поддержка обертывания асинхронных функций представлений.
-
ensure_csrf_cookie(view) -
Этот декоратор заставляет представление отправлять CSRF-куки.
Изменено в Django 5.0:Добавлена поддержка обертывания асинхронных функций представлений.
Настройки
Можно использовать ряд настроек для управления поведением CSRF в Django:
CSRF_COOKIE_AGECSRF_COOKIE_DOMAINCSRF_COOKIE_HTTPONLYCSRF_COOKIE_NAMECSRF_COOKIE_PATHCSRF_COOKIE_SAMESITECSRF_COOKIE_SECURECSRF_FAILURE_VIEWCSRF_HEADER_NAMECSRF_TRUSTED_ORIGINSCSRF_USE_SESSIONS
Часто задаваемые вопросы
Является ли отправка произвольной пары токенов CSRF (куки и данные POST) уязвимостью?
Нет, это сделано намеренно. Без атаки «человек посередине» злоумышленник не может отправить куки CSRF-токен в браузер жертвы, поэтому для успешной атаки требуется получить куки браузера жертвы через XSS или аналогичные методы, в этом случае злоумышленнику обычно не нужны атаки CSRF.
Некоторые инструменты аудита безопасности отмечают это как проблему, но, как упоминалось ранее, злоумышленник не может украсть CSRF-куки браузера пользователя. «Кража» или изменение своего собственного токена с помощью Firebug, инструментов разработчика Chrome и т. п. не является уязвимостью.
Является ли проблемой, что защита CSRF Django по умолчанию не связана с сессией?
Нет, это сделано намеренно. Отсутствие связи защиты 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/5.0/ref/csrf/