Security headers: какие HTTP-заголовки проверять на сайте

HTTP-заголовки безопасности задают браузеру ограничения: откуда разрешено загружать скрипты, можно ли открыть страницу во фрейме, сколько информации передавать при переходе и допустим ли доступ к отдельным API устройства. Проверка security headers нужна не ради максимального числа директив, а ради понятной и воспроизводимой политики защиты, которая соответствует устройству сайта.

Какую проблему решают защитные заголовки

Сервер возвращает заголовки вместе с HTML, изображениями и другими ресурсами. Браузер читает их до выполнения клиентского кода и применяет заданные ограничения. Это позволяет уменьшить последствия внедрения постороннего скрипта, запретить встраивание страницы на чужом домене, отключить определение MIME-типа по содержимому и ограничить передачу адреса исходной страницы.

Заголовки не исправляют уязвимый код и не заменяют проверку прав доступа, валидацию входных данных или безопасную работу с сессиями. Они образуют дополнительный уровень защиты. Поэтому результат проверки следует формулировать точно: заголовок присутствует, его синтаксис корректен, политика соответствует выбранной модели и не нарушает работу интерфейса.

Какие security headers проверять

Основные заголовки и критерии проверки
Заголовок Что контролирует Практический критерий
Content-Security-Policy Источники скриптов, стилей, изображений, фреймов и подключений Есть ограничивающие директивы; отсутствуют ненужные широкие разрешения; все функции сайта проверены
Strict-Transport-Security Принудительное обращение браузера к сайту по HTTPS Отправляется только по HTTPS; max-age выбран осознанно; поддомены готовы до добавления includeSubDomains
X-Content-Type-Options Запрет MIME-sniffing Значение равно nosniff, а сервер отдает корректные Content-Type
Referrer-Policy Объем данных в заголовке Referer Выбрана явная политика, например strict-origin-when-cross-origin
Permissions-Policy Доступ страницы и фреймов к камере, микрофону, геолокации и другим функциям Неиспользуемые возможности отключены, нужные интеграции не заблокированы
frame-ancestors в CSP Домены, которым разрешено встраивать страницу Указано 'none', 'self' или ограниченный список доверенных источников
X-Frame-Options Устаревшая защита от нежелательного встраивания DENY или SAMEORIGIN для совместимости; основное правило задано через CSP

Content-Security-Policy: самый чувствительный заголовок

CSP способен заметно снизить риск выполнения постороннего кода, но именно он чаще всего ломает интерфейс при поспешном включении. Базовая политика может содержать default-src 'self', отдельные правила script-src, style-src, img-src, connect-src, а также object-src 'none' и base-uri 'self'. Универсального готового значения нет: допустимые источники зависят от аналитики, карт, платежных форм, CDN и других интеграций.

Разрешения *, 'unsafe-inline' и 'unsafe-eval' ослабляют политику. Иногда они временно необходимы старому приложению, но их наличие следует зафиксировать как технический долг. Для встроенных скриптов предпочтительны nonce или хеши. Начинать безопаснее с Content-Security-Policy-Report-Only: браузер сообщает о нарушениях, но не блокирует ресурсы. После анализа отчетов и консоли политика переводится в режим блокировки.

Дополнительная изоляция

Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy и Cross-Origin-Embedder-Policy усиливают межсайтовую изоляцию. Их нельзя добавлять механически: они способны повлиять на всплывающие окна, авторизацию через внешнего провайдера, виджеты и загрузку ресурсов с CDN. Такие заголовки проверяют после основных политик и только вместе с функциональными тестами.

Диагностический алгоритм

  1. Зафиксируйте точки проверки. Возьмите главную страницу, типовую внутреннюю страницу, форму входа или заказа, страницу ошибки и URL после редиректа. Заголовки могут различаться между приложением, CDN и обработчиком ошибок.
  2. Получите фактический ответ сервера. Для проверки GET-запросом используйте:
    curl -sS -D - -o /dev/null https://example.ru/page
    Для цепочки перенаправлений добавьте -L. Проверяйте каждый ответ в цепочке отдельно: промежуточный редирект и конечная HTML-страница могут формироваться разными системами.
  3. Сверьте ответ в браузере. В DevTools откройте вкладку Network, перезагрузите страницу и изучите Response Headers у основного документа. Консоль покажет ресурсы, заблокированные CSP, и часть конфликтов политик.
  4. Оцените не только наличие, но и значение. Пустой CSP, чрезмерно широкие источники или HSTS с символическим max-age не дают ожидаемой защиты. Одновременно слишком жесткая политика может заблокировать форму, шрифт или запрос к API.
  5. Вносите изменения по одному уровню. Сначала настройте заголовок на тестовом окружении, затем проверьте ключевые сценарии, разверните изменение и повторно снимите ответы с публичных URL.

Чек-лист безопасного внедрения

  • CSP соответствует реальным источникам ресурсов и сначала проверена в режиме Report-Only.
  • В script-src нет необоснованных *, 'unsafe-inline' и 'unsafe-eval'.
  • Встраивание ограничено директивой frame-ancestors; совместимость с X-Frame-Options учтена.
  • X-Content-Type-Options: nosniff сопровождается корректными MIME-типами CSS, JavaScript и файлов.
  • Политика Referrer-Policy явно задана и не раскрывает лишние части URL внешним сайтам.
  • Permissions-Policy запрещает неиспользуемые функции, но сохраняет работу необходимых виджетов.
  • Заголовки присутствуют на успешных ответах, редиректах и страницах ошибок там, где это применимо.
  • После изменения проверены меню, формы, авторизация, поиск, аналитика, внешние виджеты и запросы API.

Типовые ошибки конфигурации

Копирование чужого набора. Политика другого проекта отражает его домены и интеграции. На новом сайте она либо ничего не ограничит, либо заблокирует необходимые ресурсы.

Дублирование на разных уровнях. Заголовок может добавляться приложением, веб-сервером и CDN одновременно. Для некоторых заголовков несколько значений меняют итоговую интерпретацию. Источник каждого значения нужно определить и оставить единую управляемую конфигурацию.

Проверка только главной. Административные маршруты, формы, страницы ошибок и ответы из кеша могут иметь другой набор заголовков. Репрезентативная выборка URL надежнее единичного теста.

Резкое включение строгой CSP. Ошибки часто проявляются не при загрузке страницы, а после открытия модального окна, отправки формы или входа через внешний сервис. Поэтому нужен список пользовательских сценариев, а не только визуальный осмотр первого экрана.

Ставка на устаревшие директивы. Например, X-XSS-Protection не следует считать современной заменой CSP. Устаревшие или неподдерживаемые механизмы не должны создавать ложное ощущение закрытого риска.

Границы метода

Анализ заголовков описывает только поведение браузера при обработке HTTP-ответа. Он не подтверждает отсутствие XSS, ошибок авторизации, утечки секретов, уязвимых зависимостей или небезопасной серверной логики. В этой статье также не рассматриваются настройка сертификатов, mixed content и общий аудит безопасности: это отдельные задачи с собственными критериями.

Публичная проверка видит то, что сервер отдает конкретному URL и клиенту. Ответ может зависеть от региона, авторизации, эксперимента, прокси или CDN. Для критичных маршрутов результаты стоит дополнять проверкой конфигурации на стороне инфраструктуры и тестами в разных состояниях пользователя.

Итог

Хорошая конфигурация security headers — это не самый длинный список заголовков, а минимальный набор осознанных ограничений. Начните с инвентаризации ответов, внедряйте CSP через Report-Only, проверяйте значения на нескольких маршрутах и подтверждайте работу ключевых функций после каждого изменения. Итоговую конфигурацию полезно хранить рядом с кодом или инфраструктурными настройками, чтобы изменения проходили ревью и повторную проверку.

SiteVisor анализирует публичный URL без доступа к CMS и помогает увидеть технические, SEO/GEO и конверсионные сигналы страницы. Чтобы получить исходную точку для дальнейшей ручной диагностики, запустите бесплатную проверку. Другие практические материалы собраны в блоге SiteVisor.