Улучшение безопасности сайта Django с помощью заголовков запросов
Это версия статьи для блога, моей презентации который я делал на DjangoCon Europe 2019 10 апреля.
Веб представляет собой развивающуюся платформу с множеством проблем с обратной совместимостью. Новые методы веб-безопасности часто приходят от осознания того, что у старого функционала есть некоторые недостатки. Вместо того, чтобы нарушать работу старых сайтов, изменяя проблемный функционал, есть множество более безопасных вариантов развития. Вы можете улучшить безопасность сайта с помощью модификации заголовков HTTP.
Securityheaders.com — инструмент, созданный консультантом по безопасности Скотт Хелм для сканирование и создания отчета о безопасности на основе заголовков запросов. Он дает любому URL оценку от F до A +, что является хорошим и простым способом оценки состояния безопасности вашего сайте. Хотя, как и в случае любого другого автоматизированного отчета, для его оценки требуется некоторая человеческая интерпретация.
(Я бы также порекомендовал сканер безопасности Mozilla Observatory, но в этой статье я его не использую, потому что он проводит проверку по гораздо большим числом параметров чем просто заголовки запросов.)
Например, Yahoo получает оценку A + на Securityheaders.com:
А Google на момент написания статьи набрал только C:
Это статья о том, как настроить типичное веб-приложение Django для оценки этого сканера. Вы можете легко, защитить своих пользователей и произвести впечатление на своего босса или клиентов!
В этой статье мы рассмотрим 6 заголовков, которые нужно установить, чтобы набрать A+ (на момент написания статьи). Так же, мы рассмотрим бонусный экспериментальный седьмой заголовок …
1. X-XSS-Protection
Межсайтовый скриптинг (Cross-Site Scripting), или XSS, — это метод, который злоумышленники могут использовать для внедрения своего собственного кода на ваш сайт. С помощью этого вида атаки можно например, добавить ложный контент или шпионить за пользователями, чтобы украсть их пароли.
Поскольку XSS является таким распространенным видом уязвимости веб-сайтов, браузеры добавили функции для его обнаружения и предотвращения в некоторых случаях, включенные в их «XSS Auditors». По умолчанию они включены, поэтому блоки скриптов, которые выглядят как атаки XSS, блокируются, но при этом страница продолжает работать.
Настройка заголовка X-XSS-Protection для использования «block mode» обеспечивает дополнительную безопасность. Это настройка указывает браузеру полностью блокировать страницы с обнаруженными атаками XSS, в случае, если они содержат другие проблемные элементы. Например, см. демонстрацию атаки Скотта Хельма, где HTML отправляется в GET-параметре и появляется на странице, которая затем блокируется.
Этот заголовок обеспечивает дополнительную защиту сайта. Даже если ваш веб-сайт защищен от XSS, если он запускает XSS auditor браузера, его все равно следует использовать, чтобы избежать этот тип атаки.
В Django уже встроенный этот параметр, и его легко активировать. Вы так же можете увидите предупреждение security.W007 при запуске команды manage.py check —deploy, если этот параметр не активирован.
ВключениеX-XSS-Protection:
- Вам нужно прописать django.middleware.security.SecurityMiddleware в настройках MIDDLEWARE, как можно выше. Это настройка уже включена по умолчанию в шаблоне startproject.
- Так же добавьте SECURE_BROWSER_XSS_FILTER = True в ваш файл settings.py .
Больше информации смотрите в документации Django, например, предостережения о медиа-файлах.
2. Strict-Transport-Security
HTTP Strict Transport Security, или HSTS, — это способ указать браузеру загружать ваш сайт только по HTTPS. Когда браузер увидит этот заголовок на веб-сайте, он будет отправлять только HTTPS-запросы. Так же в заголовке указывается максимальный срок хранения в секундах, который ограничивает время, в течение которого браузер будет помнить об этом. Это параметр предотвращает перехват HTTP-запросов злоумышленником или посредником AITM для предоставления вредоносного контента в вашем домене.
Вместе с задание заголовка Strict-Transport-Security со значением max-age, вы можете установить еще пару флагов:
- includeSubDomains позволяет указать все субдомены вашего домена.
- preload говорит браузерам хранить ваш домен в базе данных известных доменов только для strict-only использования. Этот параметр может быть установлено только в доменах верхнего уровня, таких как example.com. Браузеры, открывающие URL-адреса на предварительно загруженных доменах HSTS, никогда не будут отправлять HTTP-запросы с поддержкой AITM. Как только этот флаг установлен, вы отправляете свой домен всем браузерам только через службу предварительной загрузки Google.
В настоящее время некоторые домены верхнего уровня TLD (top—level domain), такие как .app, сами preloaded, что означает, что они поддерживают только HTTPS-сайты.
Опять же, этот заголовок уже встроен в Django. Вы увидите предупреждение security.W004, если не установили максимальный возраст, security.W005 при отсутствие флага includeSubDomains или security.W021 при отсутствие флага preload.
Включение Strict-Transport-Security:
- Убедитесь что прописано SecurityMiddleware как указано в предыдущем параметре.
- Установите для SECURE_HSTS_SECONDS количество секунд, которое вы хотите указать в заголовке.
- Не обязательно, установите SECURE_HSTS_INCLUDE_SUBDOMAINS и SECURE_HSTS_PRELOAD в True что бы активировать соотвествующие флаги.
Предупреждение. Этот параметр не совсем тот, который вы можете просто включить не задумывая о последствиях, особенно если у вас используются субдомены! По этому поводу даже говориться в документации Django security.W004:
… небрежное включение HSTS может вызвать серьезные, необратимые проблемы
Если вы преждевременно активируете его, вы заблокируете пользователей, выполняющих обычные HTTP-запросы. Браузер начнет блокировать их, пока вы не удалите этот параметр в заголовке и не истечет максимальное количество секунд.
Поэтому вы должны постепенно увеличивать SECURE_HSTS_SECONDS, проверяя, что ничто не ломается. Делайте это в течение нескольких дней, проверяя обратную связь с вашими пользователями. Начните с чего-то небольшого, например, 30 секунд, и добавляйте до 31536000 секунд (то есть 1 год).
Добавление флагов так же зависит от вашей ситуации. Если его не имеет смысла включать, отключите соответствующее предупреждение с помощью SILENCED_SYSTEM_CHECKS.
Для получения дополнительной информации см. Документацию Django HTTP Strict Transport Security.
3. X-Content-Type-Options
Браузеры пытаются угадать тип содержимого ответов, если сервер, отправляет неправильный ответ браузер пытается распознать его — эта функция, называемая MIME Sniffing. Иногда, если браузер не может угадает правильный тип контента это может приводить к ошибкам. Например, если загруженное пользователем изображение на вашем сайте интерпретируется как HTML, в итоге становиться возможным проведение XSS атаки.
Первоначально у каждого браузера были свои правила определение контента, они были приведены в соответствие со спецификацией WHATWG.
Установка заголовка X-Content-Type-Options на nosniff выключает автоопределение контента MIME Sniffing (в большинстве случаев). Так как хорошо построенный веб-сайт не нуждается в таком функционале, вы всегда должны использовать nosniff.
Этот заголовок также встроен в Django, и вы увидите предупреждение security.W006, если вы его не включили.
Включение X-Content-Type-Options:
- Убедитесь что прописано SecurityMiddleware как указано в предыдущем параметре.
- Установите SECURE_CONTENT_TYPE_NOSNIFF = True в вашем settings.py
Больше информации смотрите больше в документации по Django X-Content-Type-Options.
4. X-Frame-Options
Clickjacking — это метод, при котором злоумышленник обманывает вашего пользователя, заставляя его щелкнуть на что-то на вашем сайте. Обычно это делается путем встраивания вашего сайта в <iframe> на сайте злоумышленника.
Например, см. пост в блоге Троя Ханта. В нем описывается банковское приложение, прозрачно размещенное в рамке перед кнопкой «Win an iPad». Когда пользователь пытается получить приз, он нажимает кнопку «перевести деньги» на веб-сайте банка.
Существуют различные методы предотвращения clickjacking, но лучше всего добавить параметр X-Frame-Options в заголовке. Это позволит отключить ваш сайт в случае использование его в <iframe> или только в списке разрешенных доверенных доменов.
Этот заголовок также встроен в Django. Вы увидите предупреждение security.W002, если у вас нет middleware, или security.W019, если у вас значение этого параметра не установлено в DENY.
Включение X-Frame-Options:
- Вам нужно прописать django.middleware.clickjacking.XFrameOptionsMiddleware в настройках MIDDLEWARE как можно выше. По умолчанию это уже прописано в шаблоне startproject.
- Установите X_FRAME_OPTIONS = ‘DENY’ в вашем settings.py
Существуют дополнительные параметры для включения определенных доменов в <iframe> вашего сайта или для отключения защиты для определенных вьюх. Смотрите подробнее в документации по Django Clickjacking Protection.
5. Referrer-Policy
Заголовок Referer сообщает веб-сайту URL-адрес, с которого пришел пользователь. Это отлично подходит для аналитики, так как вы можете узнать, откуда приходят посетители вашего сайта, анализируя это значение.
Но это также ужасно с точки зрения конфиденциальности, так как веб-сайты, на которые вы ссылаетесь, получают URL-адреса пользователей. URL может содержать конфидициальную информацию, например adamj.eu/illuminati-funding или /staff-admin/users/?q=user@example.com. Кроме того, если пользователь посещает сайт HTTP с вашего HTTPS-сайта, AITM может читать эти URL-адреса.
Установка заголовка Referrer-Policy позволяет указать браузеру, когда отправлять Referer.
Предупреждение: слово «referrer» имеет два «r» в середине. Оригинальная спецификация HTTP содержала опечатку с одним «r», и никто не заметил, поэтому заголовок пишется как Referer. Чтобы не повторять эту опечатку, авторы Referrer-Policy были более осторожны, и включили два «r». Хотя на самом деле это создало больше путаницы, чем пользы.
Этот заголовок не встроен в Django, но вы можете добавить его с помощью пакета django-referrer-policy от другого разработчика Джеймса Беннетта. Следуйте инструкции в документации к пакету, чтобы добавить его в middleware и его настройки.
Имейте в виду, что наиболее безопасное значение no-referrer нарушает CSRF Django (см. «Удаление заголовка Referer» в документации CSRF). Возможно вы, не захотите использовать этот параметр.
Я рекомендую использовать same-origin, который отправляет Referer только для запросов в тот же домен. Это не нарушает CSRF и позволяет внутренней аналитике работать без потери значений Referer для других доменов.
6. Content-Security-Policy
Content Security Policy, или CSP, — это политика, которая блокирует некоторый контент. Она помогает остановить XSS, clickjacking и другие виды инъекционных атак.
По умолчанию браузеры позволяют веб-сайтам загружать контент из любого места. Это означает, что если злоумышленник успешно выполнит XSS-атаку на вашем сайте, он сможет встроить код из любого сайта в Интернете. CSP — это набор директив, которые определяют списки разрешенных доменов, с которых браузер может загружать контент. Что позволяет сильно ограничивать такие атаки.
Например, возьмите эту политику из документации MDN:
default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com
Здесь есть четыре директивы, разделенные точкой с запятой. Они говорят браузеру:
- По умолчанию загружать ресурсы только из текущего домена
- За исключением изображений, которые можно загрузить с любого домена
- Медиа может загружаться только с media1.com и media2.com (а не с текущего домена)
- Скрипты могут загружаться только с userscripts.example.com
Сама политика прописывается в заголовке Content-Security-Policy.
Этот параметр не встроен в Django, но его можно установить с помощью пакета django-csp от Mozilla. Следуйте инструкциям по установке и настройке в его документации. Он включает в себя middleware и множество настроек для создания директив политики.
В спецификации CSP есть много разных директив и опций, и django-csp использует 24 параметра настроек. Я бы порекомендовал прочитать документацию в MDN и разобраться в вариантах. Вот несколько рекомендаций.
Новый сайт
Если вы настраиваете совершенно новый сайт без внешних ресурсов, вам будет гораздо легче. Вы должны начать с ограничительного CSP и открывать дополнительные параметры по мере развития сайта.
В начале можно заблокировать загрузку внешних ресурсов всех видов. Затем вы можете настроить свою политику CSP нужным образом.
Вы можете начать с стандартных настроек django-csp, в которых есть только CSP_DEFAULT_SRC = [«‘self'»]. Это блокирует большинство внешних ресурсов.
Или вы можете начать с более строгого CSP, например, рекомендованного Google Strict CSP Page. Это еще больше защитит ваш сайт.
Существующий сайт
CSP сложнее добавить к существующему сайту, и чем больше сайт, тем труднее задача.
Первым шагом является создание исходного CSP. Вы можете использовать одну из приведенных выше ограничительных рекомендаций или сделать более обоснованное предположение с помощью таких инструментов, как CSP Toolkit или CSPIsAwesome.
Затем включите этот параметр в режиме только для отчетов, установив параметры django-csp CSP_REPORT_ONLY и CSP_REPORT_URI. Браузеры ваших пользователей будут проверять, но не применять политику, а затем сообщать вам о нарушениях.
Для активирования режима только для чтения в django-csp, установите CSP_REPORT_ONLY = True и CSP_REPORT_URI URL-адрес веб-хука. Этот URL будет получать отчеты от ваших пользователей в формате JSON.
Вы можете попытаться получать отчеты самостоятельно, но я бы не рекомендовал это делать. Вместо этого используйте сервис, который может обработать все отчеты и представить их вам в удобной панели инструментов. Два из которых я знаю:
- метко названный report-uri.com, создатель Securityheaders.com
- Sentry, известный инструмент, для дебагинга ошибок на стороне сервера
Как только вы создадите эту отчетность, вы можете выполнять итерационную настройку CSP до тех пор, пока не увидите больше нарушений. Затем скажите браузерам принудительно включить его, отключив режим только для отчетов. Отчеты будут по-прежнему полезны после этого, когда ваш сайт будет развиваться или подвергается атакам XSS.
7. Дополнительная настройка Feature-Policy
Настройка вышеупомянутых шести заголовков даст вам рейтинг A+ на Securityheaders.com. Это финальный заголовок, так же может помочь вам обеспечить безопасность вашего сайта, но в настоящее время он экспериментальный.
Feature Policy — это еще одна политика, которая управляет функциями браузера, такими как автоматическое воспроизведение видео или доступ к веб-камере. Она позволяет вам отключить их для вашего сайта или тех, которые находятся в вашем <iframe>.
На момент написания статье, этот параметр поддерживается не всеми браузерами. Chrome — как наиболее популярный браузер, реализующий его, но он требует, чтобы у пользователя был включен флаг «экспериментальные веб-функции».
Чтобы включить его в Django, используйте пакет django-feature-policy . Для этого требуется установка middleware и настройка FEATURE_POLICY. См. документацию по пакету, а также документацию по MDN, чтобы узнать, что делает каждая из его функций.
Я бы рекомендовал полностью отключить некоторые раздражающие функции, такие как autoplay, camera, fullscreen, geolocation и microphone (при условии, что ваш сайт их не использует). Это означает, что встраиваемые вами сценарии, например от рекламных партнеров, не будут раздражать ваших пользователей этими функциями.
Поскольку параметр является экспериментальным, если вы его добавите, по чаще обновляйте django-feature-policy. Я регулярно обновляю его, чтобы следить за изменениями в спецификации. Они недавно выпустили версию 2.0.0, так как несколько функций изменилось (см. changelog).
Заключение!
Я надеюсь, что эта статья помогла вам повысить уровень знаний в области веб-безопасности и найти время, чтобы перевести свой веб-сайт на оценку A+!
Оригинал: How to Score A+ for Security Headers on Your Django Website