Генератор CSP
Дайте нам адрес реальной страницы. Мы прочитаем ее HTML, перечислим все места, откуда она загружает скрипты, стили, изображения, шрифты и фреймы, и напишем политику, которую можно опробовать в режиме report-only, где она ничего не сломает.
Список гостей для ваших страниц
Браузер делает все, что велит страница. Если в HTML написано «запусти этот скрипт с того сервера», он запустится. Это становится опасным в тот день, когда кому-то удается добавить строку на вашу страницу: через неочищенное поле комментария, взломанный плагин или рекламу. Браузер никак не может отличить их строку от вашей.
Content-Security-Policy (CSP) это заголовок ответа, в котором вы заранее говорите браузеру, откуда этой странице разрешено что-либо загружать. Он читается как список гостей, по строке на каждый вид ресурсов:
default-src 'self';
script-src 'self' https://cdn.example.com;
img-src 'self' data: https://images.example.net;
object-src 'none'Каждая строка называется директивой. 'self' означает собственный источник (origin) страницы. Все, чего нет в списке, отклоняется, а отказ записывается в консоль браузера.
Как эта страница составляет черновик
Наш сервер один раз загружает адрес и разбирает HTML так, как это сделал бы браузер, но ничего не выполняя. Каждый найденный адрес обрезается до источника (схема и хост, без пути) и раскладывается по директивам:
| Директива | Откуда берется |
|---|---|
script-src | <script src>, предзагрузка модулей и адреса скриптов, указанные во встроенных фрагментах-загрузчиках |
style-src | ссылки на таблицы стилей и правила @import |
img-src, font-src | теги изображений, srcset, иконки, постеры, предзагрузки и url() во встроенном CSS и в первых четырех таблицах стилей с собственного источника сайта |
frame-src, media-src, object-src, manifest-src | фреймы, аудио и видео, плагины, ссылка на манифест |
form-action, base-uri | адреса отправки форм и тег <base> |
connect-src | только подсказки preconnect и задокументированные адреса нескольких сервисов, найденных на странице (Google Analytics, Google Fonts, Cloudflare Web Analytics) |
Черновик начинается с default-src 'self', добавляет по строке на директиву и закрывает двери, у которых нет запасного варианта: object-src 'none', base-uri 'self', frame-ancestors 'self', а на страницах HTTPS еще upgrade-insecure-requests. Если страница уже отправляет политику, в заголовке или в теге meta, мы показываем ее и сравниваем с тем, что страница загружает.
Самое трудное: встроенный код
Внедренный код почти всегда встроенный: блок <script> или атрибут onclick, вписанный прямо в страницу. Значит, политика, которая чего-то стоит, запрещает встроенный код. Загвоздка в том, что у большинства сайтов полно собственного встроенного кода. Инструмент считает блоки скриптов, атрибуты-обработчики событий, ссылки javascript:, блоки стилей и атрибуты style=, а затем выдает два черновика.
- В строгом черновике нет
'unsafe-inline'. К нему вы и хотите прийти, но он сломал бы подсчитанный выше встроенный код. - Переходный черновик добавляет
'unsafe-inline'там, где странице это нужно. Он по-прежнему ограничивает, с каких сайтов можно загружать ресурсы, но уже не останавливает внедренные встроенные скрипты, поэтому помечен как слабый.
Перейти от одного к другому можно тремя способами. Вынести код в файлы, поручить серверу помечать каждый тег одноразовым значением nonce для каждого ответа или перечислить в политике SHA-256 хеш каждого блока. Инструмент вычисляет эти хеши для найденных блоков скриптов. Они помогают только для блоков, текст которых никогда не меняется.
Сначала report-only, всегда
Отправляйте черновик как Content-Security-Policy-Report-Only. Браузер ничего не блокирует и только записывает, что заблокировал бы. Пройдитесь по сайту с открытой консолью, добавьте то, чего законно не хватает, и повторяйте, пока журнал не затихнет. Затем переименуйте заголовок в Content-Security-Policy. Вы получите фрагменты для Apache, nginx и тега meta. Вариант с meta не умеет ни report-only, ни frame-ancestors.
Черновик составлен по одной странице, прочитанной один раз, без выполнения кода. Он не видит запросов, которые скрипты делают после загрузки (маячки аналитики, виджеты чатов, платежные фреймы, ленивые изображения), того, что получают вошедшие пользователи, и других страниц сайта. Будьте готовы добавлять источники. Оценку заголовков, которые страница отправляет сегодня, дает оценка заголовков безопасности.
Частые вопросы
Сломает ли Content-Security-Policy мой сайт?
Действующая политика блокирует все, что в ней не указано, так что неверная сломает. Политика report-only ничего сломать не может, потому что только записывает. Начните с нее и переходите к блокировке, когда консоль перестанет показывать нарушения на основных страницах.
Есть ли смысл в политике с unsafe-inline?
Отчасти. Она по-прежнему ограничивает, куда могут идти скрипты, фреймы и отправка форм, блокирует плагины и не дает перехватить страницу через тег <base>. Но она не остановит злоумышленника, который может вписать блок скрипта в страницу, а это главная причина заводить политику. Используйте ее как промежуточный шаг.
Чем nonce отличается от хеша?
Nonce это случайное значение, которое сервер генерирует для каждого ответа и повторяет в каждом теге скрипта, которому доверяет. Хеш это отпечаток одного конкретного блока кода, указанный в политике. Nonce подходит для страниц, которые собираются при каждом запросе. Хеши подходят для нескольких статичных фрагментов на страницах, которые могут кэшироваться.
Почему в сгенерированной политике нет путей?
CSP допускает пути вроде https://cdn.example.com/lib/, но после перенаправления они игнорируются и ломаются, как только файл переезжает. Большинство реальных политик перечисляют источники. Если источник хранит файлы многих сторон, как публичный CDN, дополнительно закрепите точные файлы хешами целостности.
Можно ли настроить CSP в WordPress или другой CMS?
Да, через настройки веб-сервера или плагин безопасности, который отправляет заголовки. Тема и плагины принесут длинный список встроенных скриптов и стилей, поэтому начните с заголовка report-only и переходного черновика, а затем сокращайте встроенный код по одному плагину.
Где посмотреть, что блокирует политика?
Во вкладке Console инструментов разработчика браузера. Каждое нарушение называет директиву и заблокированный адрес. Чтобы собирать их у посетителей, добавьте директиву report-uri или report-to, указывающую на сборщик отчетов.