Webrium
Подписаться

Заголовки безопасности: какие ставить и что они ломают

Что ломается при неправильной настройке заголовков безопасности и как этого избежать

Схема разделенного поля: слева заголовки безопасности добавлены, справа сайт не работает из-за неправильной настройкиЗаголовки добавленыСайт сломался

Заголовки безопасности: что они делают и почему не работают «из коробки»

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

Начнём с того, что заголовки безопасности — это не серебряная пуля. Они не заменяют проверку ввода на сервере, не защищают от утечек данных через сторонние сервисы и не спасают от уязвимостей в коде. Но они могут усложнить жизнь злоумышленникам, если их правильно настроить.

Тупик: почему просто добавить заголовки недостаточно

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

  1. Сайт ломается. Например, если добавить CSP в режиме запрета без предварительной настройки, перестанут работать встроенные скрипты, виджеты, аналитика. Пользователи увидят пустую страницу или неработающие элементы интерфейса.

  2. Заголовки конфликтуют друг с другом. Например, X-Frame-Options: DENY и CSP с frame-src 'self' противоречат друг другу: первый запрещает встраивание вообще, второй разрешает его на своём домене. Браузер не знает, какое правило применять, и может выбрать более строгое.

  3. Заголовки не работают из-за неверных настроек. Например, если установить слишком маленькое значение max-age для HSTS, браузер будет часто проверять правило, что увеличит нагрузку на сервер. Если установить слишком большое, браузер может не заметить изменения в настройках сервера.

  4. Заголовки не защищают от реальных атак. Например, CSP не спасёт от XSS, если на сайте есть уязвимость в коде, которую можно эксплуатировать напрямую. Заголовки безопасности — это дополнительный слой защиты, а не замена базовым мерам.

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

Какие заголовки действительно работают

Content-Security-Policy (CSP)

CSP — это политика содержимого, которая ограничивает источники загрузки ресурсов: скриптов, стилей, изображений, шрифтов. Она нужна, чтобы предотвратить межсайтовый скриптинг (XSS): если браузер не загрузит скрипт с чужого домена, злоумышленник не сможет внедрить вредоносный код.

Но CSP — это не просто заголовок, который можно включить и забыть. Если включить его сразу в режиме запрета, сайт сломается. Поэтому CSP внедряют в три этапа:

  1. Режим отчёта (Content-Security-Policy-Report-Only). Браузер не блокирует ресурсы, но отправляет отчёты о нарушениях на указанный URL. Это позволяет понять, какие ресурсы нарушают политику, и исправить их до включения запрета.
  2. Разбор нарушений. На этом этапе нужно проанализировать отчёты и исправить проблемы: заменить встроенные скрипты на внешние, добавить разрешения для сторонних доменов, обновить библиотеки.
  3. Режим запрета (Content-Security-Policy). Только после того, как все нарушения устранены, можно включить CSP в режиме запрета.

Пример CSP в режиме отчёта:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://cdn.example.com; report-uri /csp-report-endpoint

Что ломается при включении CSP:

  • Встроенные скрипты. Их нужно вынести в отдельные файлы.
  • Сторонние виджеты. Для них нужно добавить разрешение на загрузку с их домена.
  • Аналитика. Для неё тоже нужно добавить разрешение на загрузку с домена аналитического сервиса.
  • Стили и шрифты, загружаемые с CDN. Их нужно либо разместить на своём домене, либо добавить разрешение на загрузку с CDN.

Strict-Transport-Security (HSTS)

HSTS — это заголовок, который принудительно переводит все соединения на HTTPS. Он нужен, чтобы предотвратить атаки через понижение протокола (downgrade attacks): если злоумышленник сможет перехватить соединение и заставить браузер использовать HTTP, он сможет украсть куки или внедрить вредоносный код.

HSTS работает так: когда браузер получает этот заголовок, он запоминает, что сайт должен открываться только по HTTPS, и автоматически переводит все HTTP-запросы на HTTPS. Заголовок выглядит так:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Что означают параметры:

  • max-age — время, в течение которого браузер будет помнить о необходимости использовать HTTPS. Обычно ставят значение, достаточное для того, чтобы браузер не забыл правило до следующего обновления сертификата. Если установить слишком маленькое значение, браузер будет часто проверять правило, что увеличит нагрузку на сервер. Если установить слишком большое, браузер может не заметить изменения в настройках сервера.
  • includeSubDomains — распространяет правило на все поддомены.
  • preload — включает сайт в предзагруженный список HSTS. Это значит, что браузер будет знать о необходимости использовать HTTPS ещё до первого посещения сайта.

Что ломается при включении HSTS:

  • Если на сайте есть страницы, которые должны открываться по HTTP, они перестанут работать.
  • Если сертификат SSL недействителен, браузер не сможет открыть сайт вообще.
  • Если сайт добавлен в предзагруженный список HSTS, его нельзя будет удалить оттуда без обновления всех браузеров.

X-Frame-Options

Этот заголовок запрещает встраивать страницу в рамку (<iframe>). Он нужен, чтобы предотвратить кликджекинг: атаку, при которой злоумышленник встраивает вашу страницу в свою и обманом заставляет пользователя нажать на кнопку.

Заголовок выглядит так:

X-Frame-Options: DENY

Или так:

X-Frame-Options: SAMEORIGIN

Что означают параметры:

  • DENY — запрещает встраивание страницы в рамку вообще.
  • SAMEORIGIN — разрешает встраивание только на страницах того же домена.

Что ломается при включении X-Frame-Options:

  • Если на сайте есть страницы, которые должны встраиваться в рамки, они перестанут работать.
  • Если сайт использует сторонние сервисы, которые встраивают его в рамки, они тоже перестанут работать.

Referrer-Policy

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

Заголовок выглядит так:

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

Что означают параметры:

  • no-referrer — не отправлять заголовок Referer вообще.
  • no-referrer-when-downgrade — не отправлять Referer при переходе с HTTPS на HTTP.
  • same-origin — отправлять Referer только при переходах внутри одного домена.
  • strict-origin — отправлять только источник (домен) при переходах на другие домены.
  • strict-origin-when-cross-origin — отправлять полный URL при переходах внутри одного домена и только источник при переходах на другие домены.

Что ломается при включении Referrer-Policy:

  • Если на сайте есть аналитика, которая использует заголовок Referer для отслеживания переходов, она может перестать работать.
  • Если сайт использует сторонние сервисы, которые полагаются на Referer, они тоже могут перестать работать.

Какие заголовки ничего не делают

Некоторые заголовки безопасности остались от прошлых версий спецификаций и не влияют на поведение браузера. Вот основные из них:

X-XSS-Protection

Этот заголовок должен был включать встроенную защиту от XSS в браузерах, но современные браузеры игнорируют его. Вместо него используют CSP.

X-Content-Type-Options

Этот заголовок запрещает браузеру определять MIME-тип ресурса самостоятельно. Он нужен, чтобы предотвратить атаки через подмену типа содержимого (MIME sniffing). Но современные браузеры и так не определяют MIME-тип самостоятельно, поэтому этот заголовок ничего не делает.

Expect-CT

Этот заголовок должен был проверять сертификаты на соответствие Certificate Transparency, но современные браузеры делают это автоматически. Заголовок устарел и не влияет на поведение браузера.

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

Внедрять заголовки безопасности нужно постепенно, чтобы не сломать сайт. Вот порядок действий:

  1. Включить режим отчёта для CSP. Это позволит понять, какие ресурсы нарушают политику, и исправить их до включения запрета.
  2. Проанализировать отчёты CSP. На этом этапе нужно исправить все нарушения: заменить встроенные скрипты на внешние, добавить разрешения для сторонних доменов, обновить библиотеки.
  3. Включить CSP в режиме запрета. Только после того, как все нарушения устранены, можно включить CSP в режиме запрета.
  4. Включить HSTS. Начать с небольшого max-age, чтобы проверить, что правило работает корректно, и постепенно увеличивать его.
  5. Включить X-Frame-Options. Начать с SAMEORIGIN, чтобы не сломать виджеты на своём домене.
  6. Включить Referrer-Policy. Начать с strict-origin-when-cross-origin, чтобы не сломать аналитику.

Как проверить, что заголовки действительно отдаются

Заголовки безопасности должны отдаваться на всех страницах сайта, а не только на главной. Чтобы проверить это, можно использовать инструменты разработчика в браузере:

  1. Открыть инструменты разработчика.
  2. Перейти на вкладку «Network» (Сеть).
  3. Обновить страницу.
  4. Найти запрос к странице и проверить заголовки ответа.

Или использовать командную строку:

curl -I https://example.com/path/to/page

Что проверять:

  • Заголовки должны отдаваться на всех страницах, а не только на главной. Для этого нужно проверить несколько страниц с разными путями.
  • Заголовки должны быть корректно настроены. Например, CSP не должен блокировать нужные ресурсы, а HSTS должен иметь адекватное значение max-age.
  • Заголовки не должны конфликтовать друг с другом. Например, X-Frame-Options: DENY и CSP с frame-src 'self' будут конфликтовать.

Скопировать чужой набор заголовков целиком — плохая идея

Часто встречается совет: «Возьмите набор заголовков безопасности с чужого сайта и поставьте у себя». Это плохая идея, потому что:

  1. Чужой набор может не подходить вашему сайту. Например, если на чужом сайте нет встроенных скриптов, а на вашем есть, CSP сломает их.
  2. Чужой набор может быть устаревшим. Например, если на чужом сайте стоит X-XSS-Protection, а современные браузеры его игнорируют.
  3. Чужой набор может конфликтовать с вашими сервисами. Например, если на чужом сайте стоит Referrer-Policy: no-referrer, а ваша аналитика использует Referer, она перестанет работать.
  4. Чужой набор может быть избыточным. Например, если на чужом сайте стоит Expect-CT, который уже не нужен, это только усложнит настройку.

Вместо этого нужно настраивать заголовки безопасности под свой сайт, постепенно и с проверкой.

Позиция редакции

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

Заголовки безопасности эффективны только при соблюдении следующих условий:

  • На сайте нет уязвимостей в коде, которые можно эксплуатировать напрямую.
  • Сертификат SSL действителен и настроен корректно.
  • На сайте используются актуальные библиотеки и фреймворки.
  • Настройки сервера соответствуют требованиям безопасности.

Позиция редакции неверна, если:

  • На сайте нет реальных угроз, которые можно предотвратить с помощью заголовков безопасности. Например, если сайт статический и не содержит пользовательского ввода, XSS и кликджекинг ему не страшны.
  • Заголовки безопасности мешают работе сайта больше, чем защищают его. Например, если CSP блокирует критически важные ресурсы, а исправить это невозможно, лучше отключить CSP, чем оставить сайт неработающим.
  • На сайте уже реализованы более эффективные меры защиты. Например, если все скрипты подписаны и проверяются на сервере, CSP может быть избыточным.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

На шкале времени жизни экрана величина — момент, когда становятся известны входные данные, — пробивает пороговую линию между сервером и клиентом: если данные изсервер/клиентвремя жизни экрана
Практика

Где проходит граница клиент — сервер

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

Две угрозы по разные стороны: одна закрывается хранилищем, другая — серверомчужой скриптчужой сайт
Усиление

Токен в браузере: где его хранить

Как правильно хранить токен в браузере — и почему выбор места зависит не от удобства, а от модели угроз.

Ответ из кеша совпадает с источником или разошёлся с нимсовпалоустарело
Углубление

Кеширование по HTTP: заголовки, которые действительно работают

Как правильно кешировать по HTTP: разбираем заголовки, которые управляют сроком годности и проверкой контента — и почему их путают чаще всего.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть