0 %

Как настроить Content Security Policy на сайте: защита от XSS, безопасные скрипты и проверка ошибок

Как настроить Content Security Policy на сайте: защита от XSS, безопасные скрипты и проверка ошибок

Зачем сайту Content Security Policy

Безопасность сайта зависит не только от сложного пароля администратора, HTTPS и обновления CMS. Даже защищённый сервер может стать источником проблем, если злоумышленник добьётся выполнения своего JavaScript-кода в браузере посетителя. Для таких атак существует отдельный класс угроз, который обычно связывают с XSS, или межсайтовым выполнением сценариев.

Content Security Policy, сокращённо CSP — это политика безопасности, которую сайт передаёт браузеру через HTTP-заголовок. Она определяет, откуда разрешено загружать скрипты, стили, изображения, шрифты, фреймы и другие ресурсы. Если браузер обнаруживает попытку загрузить источник, который не соответствует правилам, он может заблокировать этот ресурс и отправить отчёт о нарушении.

CSP не заменяет обновление CMS, проверку серверного кода, защиту форм и резервное копирование. Это дополнительный уровень защиты, который ограничивает последствия ошибки в приложении или внедрённого фрагмента кода.

Для бизнеса правильно настроенная политика даёт несколько преимуществ:

  • снижает риск выполнения постороннего JavaScript-кода;
  • ограничивает загрузку ресурсов с неизвестных доменов;
  • помогает обнаружить случайные или вредоносные внешние подключения;
  • защищает посетителей от части атак через скомпрометированные страницы;
  • делает структуру подключаемых ресурсов более прозрачной;
  • помогает разработчикам поддерживать безопасную архитектуру сайта.

Особенно полезна CSP для сайтов с личными кабинетами, онлайн-оплатой, формами заявок, пользовательским контентом, административными панелями и большим количеством сторонних сервисов.

Как работает политика безопасности контента

Сайт отправляет браузеру специальный заголовок Content-Security-Policy. В нём перечисляются директивы, то есть правила для разных типов ресурсов. Например, одна директива отвечает за JavaScript, другая за изображения, третья за фреймы, а четвёртая задаёт резервное правило.

Упрощённый пример заголовка может выглядеть так:

Content-Security-Policy: default-src 'self'; img-src 'self' https:; script-src 'self'

Такое правило сообщает браузеру следующее:

  • default-src 'self' — по умолчанию разрешены ресурсы только с собственного домена;
  • img-src 'self' https: — изображения можно загружать со своего домена и по защищённому протоколу HTTPS;
  • script-src 'self' — JavaScript разрешён только с собственного домена.

В реальном проекте политика обычно сложнее. На сайте могут работать системы аналитики, платёжные виджеты, карты, онлайн-чаты, CDN, видеоплееры и шрифты. Каждый внешний сервис нужно осознанно добавить в подходящую директиву. Простое копирование чужого заголовка часто приводит либо к поломке функциональности, либо к слишком слабой защите.

Какие угрозы помогает снизить CSP

Межсайтовое выполнение сценариев

XSS-атака возникает, когда непроверенные данные превращаются в исполняемый код в браузере пользователя. Источником проблемы может быть поле комментария, форма обратной связи, параметр URL, пользовательский профиль или уязвимый шаблон.

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

Случайные внешние подключения

В проекте может появиться скрипт со стороннего домена, о котором разработчики не знают. Это бывает из-за уязвимости плагина, ошибки при сборке, изменения шаблона или компрометации внешнего сервиса. Политика позволяет ограничить список разрешённых источников и заметить неожиданные попытки загрузки.

Встраивание сайта в чужие страницы

Отдельные директивы CSP позволяют управлять тем, может ли сайт открываться внутри фрейма на другом домене. Это важно для защиты от некоторых сценариев clickjacking, когда пользователя незаметно подталкивают нажать кнопку на встроенной странице.

Утечка данных через адреса запросов

С помощью директивы connect-src можно ограничивать адреса, куда браузер отправляет запросы через Fetch, XMLHttpRequest, WebSocket и похожие механизмы. Это полезно для контроля API, аналитики и сторонних интеграций.

Основные директивы Content Security Policy

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

default-src

Это базовое правило. Оно используется для типов ресурсов, для которых не задана отдельная директива. Часто в качестве начального ограничения применяют:

default-src 'self'

Ключевое значение 'self' означает текущий источник сайта, то есть собственный протокол, домен и порт. Поддомены и другие домены автоматически разрешёнными не становятся.

script-src

Директива управляет JavaScript. Это одна из самых важных частей CSP, поскольку именно выполнение неизвестного сценария часто представляет наибольшую опасность.

В ней могут указываться:

  • 'self' — скрипты с собственного источника;
  • конкретные доверенные домены;
  • 'nonce-значение' — одноразовый идентификатор для разрешённого встроенного скрипта;
  • 'sha256-значение' — хеш конкретного встроенного скрипта;
  • 'strict-dynamic' — специальный режим доверия для сценариев, загруженных разрешённым скриптом.

Не стоит без необходимости добавлять 'unsafe-inline' и 'unsafe-eval'. Эти значения могут ослабить защиту, разрешая встроенный код или выполнение строк как сценариев. Иногда они требуются старым библиотекам или CMS, но сначала нужно проверить, можно ли обойтись без них.

style-src

Определяет, откуда можно загружать CSS. Здесь могут понадобиться собственный домен, CDN или сервис шрифтов. Если проект использует встроенные атрибуты стилей, слишком строгая политика может изменить внешний вид отдельных элементов.

img-src

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

  • data: — изображения, встроенные в код в виде Data URI;
  • blob: — временные объекты, создаваемые браузером;
  • https: — изображения с любых HTTPS-источников.

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

font-src

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

connect-src

Ограничивает адреса сетевых соединений. В эту директиву попадают API, системы аналитики, WebSocket-сервисы, чаты, сервисы отправки событий и некоторые платёжные интеграции.

frame-src и frame-ancestors

frame-src определяет, какие фреймы сайт может загружать на свои страницы. Например, это может быть видеоплеер, карта или платёжная форма.

frame-ancestors отвечает за другое: она определяет, какие сайты могут встраивать текущий сайт во фрейм. Для запрета встраивания можно использовать:

frame-ancestors 'none'

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

object-src

Директива управляет устаревшими объектами, например элементами object и embed. Для большинства современных сайтов разумным ограничением является:

object-src 'none'

base-uri

Определяет, какие значения разрешены для HTML-элемента base. Ограничение помогает избежать подмены базового адреса для относительных ссылок.

form-action

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

upgrade-insecure-requests

Инструкция просит браузер заменять небезопасные HTTP-запросы на HTTPS. Она может помочь при постепенном устранении старых ссылок, но не должна использоваться как замена полноценной настройке HTTPS и редиректов на сервере.

Почему нельзя сразу включать максимально строгую политику

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

После слишком строгого заголовка могут перестать работать:

  • счётчики посещаемости и рекламные системы;
  • карты и виджеты обратной связи;
  • онлайн-чаты и callback-сервисы;
  • платёжные формы;
  • видеоплееры;
  • внешние шрифты;
  • антиспам-проверки;
  • административные функции CMS;
  • загрузка файлов и предпросмотр изображений;
  • динамические компоненты интерфейса.

Кроме того, CSP может обнаружить старые inline-скрипты, обработчики событий в HTML, вызовы eval и ресурсы, которые разработчики забыли учесть. Поэтому внедрение нужно проводить поэтапно, начиная с инвентаризации подключений и тестового режима.

Пошаговый план внедрения CSP

Шаг 1. Составьте список ресурсов сайта

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

Зафиксируйте:

  • источники JavaScript;
  • стили и веб-шрифты;
  • домены изображений;
  • адреса API;
  • фреймы;
  • сервисы аналитики;
  • платёжные и маркетинговые интеграции;
  • WebSocket-подключения;
  • встроенные скрипты и стили.

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

Шаг 2. Уберите лишние подключения

Не нужно добавлять в CSP все домены, которые когда-либо встречались в коде. Сначала выясните, какие интеграции действительно используются. Удалите старые библиотеки, неактуальные виджеты, неиспользуемые рекламные скрипты и тестовые подключения.

Чем меньше внешних зависимостей, тем проще поддерживать безопасность, скорость и стабильность сайта.

Шаг 3. Начните с режима Report-Only

Для тестирования существует заголовок Content-Security-Policy-Report-Only. В этом режиме браузер фиксирует нарушения и показывает предупреждения, но не блокирует ресурсы.

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

Важно понимать: Report-Only не защищает сайт так же, как активная политика. Это диагностический этап, а не финальная настройка.

Шаг 4. Проанализируйте нарушения

Каждое предупреждение нужно рассматривать отдельно. Ошибка может означать:

  • действительно нужный внешний сервис, который необходимо добавить;
  • старый или лишний ресурс;
  • inline-код, который следует вынести в отдельный файл;
  • ошибку в адресе или протоколе;
  • попытку загрузить неизвестный ресурс;
  • проблему стороннего виджета.

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

Шаг 5. Настройте активную политику

После исправления нарушений можно перейти от Report-Only к обычному заголовку Content-Security-Policy. Делайте это сначала на тестовом окружении или на отдельных страницах, если архитектура проекта позволяет постепенное включение.

После активации проверьте не только загрузку страниц, но и реальные действия пользователя:

  1. отправку формы;
  2. получение уведомления;
  3. авторизацию и выход из аккаунта;
  4. добавление товара в корзину;
  5. оформление заказа;
  6. работу поиска и фильтров;
  7. загрузку файлов;
  8. отображение карт и видео;
  9. работу чата и аналитики;
  10. административные операции.

Шаг 6. Настройте сбор отчётов

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

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

Nonce и хеши для безопасных встроенных скриптов

Главная сложность строгой CSP часто связана с inline-скриптами. Полностью запрещать их безопаснее, но старые шаблоны и некоторые CMS могут использовать встроенный код.

Как работает nonce

Nonce — это случайное значение, которое сервер создаёт для конкретного ответа. Оно добавляется в CSP и в разрешённый тег script. Браузер выполняет только тот встроенный сценарий, которому соответствует правильный nonce.

Важные требования к такому подходу:

  • значение должно быть непредсказуемым;
  • оно должно создаваться заново для каждого ответа;
  • нельзя использовать один постоянный nonce на всех страницах;
  • значение не следует формировать из имени пользователя, даты или другого очевидного параметра;
  • шаблон и заголовок должны использовать одно и то же значение.

Nonce удобен для динамических страниц, где небольшое количество встроенных сценариев действительно необходимо.

Как работают хеши

Хеш позволяет разрешить конкретное содержимое встроенного скрипта. Браузер сравнивает фактический код с указанным криптографическим значением. Если содержимое изменилось, сценарий не будет считаться разрешённым.

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

Чего следует избегать в CSP

Слишком широких разрешений

Запись вроде разрешения любых HTTPS-источников может быть удобной, но она существенно расширяет область доверия. Если задача решается перечислением конкретных доменов, лучше выбрать точный список.

Постоянного использования unsafe-inline

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

Разрешения unsafe-eval без необходимости

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

Доверия ко всему CDN

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19