Django предоставляет защитные механизмы от различных атак, но не все они включены по-умолчанию. В этом посте я хочу рассмотреть какие возможности по защите предоставляет Django и как их использовать.
django.middleware.security.SecurityMiddleware
Этот middleware включен по-умолчанию и должен быть самым первым в списке, для того чтобы злонамеренные запросы блокировались до обработки другими middleware.
Он защищает сразу от многих атак и каждую из этих защит можно включить/отключить отдельно используя переменные в
settings.py
:
SECURE_BROWSER_XSS_FILTER
- по-умолчанию отключен, при включении добавляет заголовокx-xss-protection:1; mode=block
, заставляющий, поддерживающие этот заголовок браузеры, блокировать потенциально опасные запросы содержащие javascript код в POST или GET параметрах.SECURE_CONTENT_TYPE_NOSNIFF
- по-умолчанию отключен, при включении добавляет заголовокx-content-type-options:nosniff
, запрещающий браузерам угадывать Content-Type ответа сервера, что блокирует возможность неправильно интерпретировать загруженные пользователем файлы. Если у вас загруженные файлы отдаются напрямую web-сервером (например, Nginx, см. ниже), то также нужно настроить чтобы сервер отдавал этот заголовок.
server {
# ...
location /media/ {
# ...
add_header X-Content-Type-Options nosniff;
}
}
SECURE_HSTS_SECONDS
по-умолчанию отключен, добавляет заголовокStrict-Transport-Security
, который запрещает браузерам открывать сайт по нешифрованному HTTP на заданное количество секунд. Заголовок действует на весь домен, по-этому его можно включать, только если весь контент отдается по HTTPS. Этот заголовок позволяется защититься от некоторых man-in-the-middle атак.SECURE_HSTS_PRELOAD
- по-умолчанию отключен, нужно включить при добавлении сайта в захардкоженный списокHTTPS-only
сайтов, который поставляется вместе с Google Chrome. Будет работать только если задан параметрSECURE_HSTS_SECONDS
> 0.SECURE_HSTS_INCLUDE_SUBDOMAINS
- по-умолчанию отключен, при включении запрещает браузерам открывать по нешифрованному HTTP также и все поддомены сайта.SECURE_SSL_REDIRECT
- по-умолчанию отключен, при включении редиректит все HTTP запросы на HTTPS. Вероятно вам захочется делать этот редирект средствами web-сервера (например, Nginx):
server {
listen 80;
server_name example.org;
rewrite ^ http://example.org$request_uri? permanent;
}
SECURE_REDIRECT_EXEMPT
- по-умолчанию пустой список. URL адреса, соответствующие заданным в списке регулярным выражениям, не будут перенаправляться с HTTP на HTTPS, при включенииSECURE_SSL_REDIRECT
.SECURE_SSL_HOST
- по-умолчанию не задан. Если задать строку (домен), то при включенномSECURE_SSL_REDIRECT
все запросы будут перенаправляться на этот домен.
Если при включении HSTS настроек заголовки не добавляются, возможно Django не распознает, что запросы зашифрованные из-за того, что располагается за реверс-прокси. Для того, чтобы Django понимал какие запросы были зашифрованными нужно добавить параметр
SECURE_PROXY_SSL_HEADER
вsettings.py
, который содержит кортеж("header-name", "header-value")
. Получив такой заголовок от прокси сервера Django будет считать что запрос был получен по протоколу HTTPS.
django.middleware.csrf.CsrfViewMiddleware
Middleware защищает от межсайтовой подделки запроса.
При отправке любых небезопасных (POST, PUT, DELETE) запросов middleware будет проверять наличие csrf токена и если
этот токен не найден или не корректен запрос будет фильтроваться. Для отправки токена существуют разные способы.
Для обычных форм можно добавлять скрытое поле используя тег {% csrf_token %}
в форме. Для ajax запросов можно
добавлять заголовок X-CSRFToken
со значением полученным из cookie.
Если у вас SPA и вы не используете {% csrf_token %}
, то вам нужно заставить Django выдать клиенту cookie с токеном.
Для этого можно использовать view decorator ensure_csrf_cookie.
django.middleware.clickjacking.XFrameOptionsMiddleware
При включении данный middleware добавляет заголовок X-Frame-Options: SAMEORIGIN
, который запрещает современным браузерам
загружать сайт в (i)frame на сайтах с другим доменом. Также можно запретить подключение сайта со всех доменов используя
переменную X_FRAME_OPTIONS = 'DENY'
.