Назад в блог

Security headers: HSTS, CSP, X-Frame-Options и другие

· 7 мин чтения

Бэкенд - крепость, но вторая половина защиты в браузере. HSTS, CSP и другие заголовки блокируют XSS, clickjacking и MITM - и это бесплатно.

Security headers: HSTS, CSP, X-Frame-Options и другие

Почему они важны

Приложение может быть идеально защищено на стороне backend и всё равно оставаться уязвимым к атакам на стороне браузера: cross-site scripting (XSS), clickjacking, protocol downgrade, MIME confusion. Security-заголовки переносят защиту в сам браузер.

Strict-Transport-Security (HSTS)

Принудительно включает HTTPS для всех будущих посещений. После первого визита по HTTPS браузер запоминает домен и сам переписывает любую http://-ссылку на https://, даже если пользователь кликнет по плохой ссылке.

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • max-age=63072000 = действует 2 года
  • includeSubDomains = правило действует и для всех поддоменов; оно принудительно включает HTTPS везде, поэтому поддомен, работающий только по HTTP, напр. staging.example.com, станет недоступным в браузере
  • preload = позволяет включение в список HSTS preload, встроенный в Chrome / Firefox / Safari

Внимание: Удаление HSTS с preload из списка занимает 6+ месяцев. Не добавляйте preload, пока не убедитесь, что HTTPS стабильно работает на всех поддоменах.

Content-Security-Policy (CSP)

Самый мощный, но и самый сложный в настройке заголовок. Он задаёт whitelist того, откуда браузеру разрешено загружать скрипты, стили, изображения и iframe-ы. Без CSP атакующий, способный инжектировать <script> тег, может выполнить любой JS; с CSP - только код из одобренных источников.

Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-...'; img-src 'self' data: https://cdn.example.com; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'

Распространённые директивы:

  • default-src 'self' = по умолчанию разрешать только ресурсы со своего домена
  • script-src = JS-источники (для inline-скриптов укажите hash или nonce)
  • style-src = CSS-источники
  • img-src = изображения
  • connect-src = AJAX, WebSocket, EventSource
  • frame-ancestors 'none' = никто не может встроить страницу в iframe (лучше, чем X-Frame-Options)
  • report-uri /csp-report = браузер отправляет JSON при каждом нарушении (вы ловите его в backend и логируете)
  • Примечание: report-uri сегодня deprecated - браузеры с поддержкой report-to его игнорируют. Современный способ - директива report-to плюс отдельный заголовок Reporting-Endpoints; оставьте report-uri только как fallback для Firefox, который пока не поддерживает report-to.

X-Frame-Options

Более старая альтернатива frame-ancestors. Защищает от clickjacking, когда атакующий встраивает вашу страницу как iframe и перекрывает её невидимыми кнопками.

X-Frame-Options: DENY

Значения: DENY (никто), SAMEORIGIN (только мой домен), ALLOW-FROM uri (deprecated).

X-Content-Type-Options

X-Content-Type-Options: nosniff

Отключает MIME sniffing, поэтому браузер уважает Content-Type, возвращённый сервером. Без этого атакующий может загрузить файл с неправильным типом (например, изображение, которое на самом деле HTML со скриптом), и браузер может выполнить его как веб-страницу.

Referrer-Policy

Referrer-Policy: strict-origin-when-cross-origin

Управляет тем, сколько информации о предыдущей странице браузер отправляет в заголовке Referer при клике. Значение по умолчанию в современных браузерах уже strict-origin-when-cross-origin, но явная установка заголовка гарантирует согласованное поведение.

Permissions-Policy

Отключает API, которые вам не нужны, такие как камера, микрофон, GPS и геолокация. Атакующий, использующий XSS, не может запросить эти API.

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()

Практическая конфигурация в nginx

server {
  listen 443 ssl http2;
  server_name example.com;

  add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
  # preload здесь намеренно опущен - добавляй его только когда уверен на 100% (см. ловушку с preload выше)
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-Frame-Options "DENY" always;
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

  # CSP обширна - определяйте её согласно вашему приложению
  add_header Content-Security-Policy "default-src 'self'; script-src 'self'; ..." always;
}

Ключевое - always: без него nginx пропускает заголовки при error response (4xx, 5xx).

Аудит текущего состояния

Проще всего через онлайн-инструмент. Наш header check покажет вам, какие заголовки отсутствуют и какие неправильны. Для тщательного аудита securityheaders.com дополнительно даёт оценку A-F.

Вывод

Security-заголовки - одна из лучших инвестиций в безопасность по соотношению усилий и выгоды: обычно полчаса настройки в reverse proxy против целой категории атак на стороне браузера. Аудит раз в полгода в рамках регулярного обслуживания - разумный минимум.

Бесплатный аудит security headers

Без регистрации, результат за три секунды.

Протестировать сайт →

Поделиться: Ссылка скопирована

Попробуйте ePulz.io бесплатно - 7 дней без банковской карты.

Создать аккаунт