CSP для владельцев веб-приложений
Спланируйте и внедрите CSP с понятными ответственными, отчетами, проверками совместимости и границами отката.
BotBrowser Team
Нужна структурированная документация по теме Развертывание?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
Content Security Policy (CSP, политика безопасности содержимого) представляет собой политику ответа приложения, которая сообщает поддерживающему ее браузеру, какие типы ресурсов документ может загружать или выполнять. Она может уменьшить последствия некоторых ошибок внедрения, ограничивая сценарии, соединения, фреймы, Workers и другие ресурсы. Но CSP не заменяет полноценную программу безопасности: авторизация на сервере, кодирование вывода, проверка зависимостей, дизайн сеансов, безопасность транспорта и реагирование на инциденты остаются отдельными задачами. Спецификация W3C CSP Level 3 определяет модель, а руководство MDN по CSP описывает практические варианты внедрения.
Считайте CSP решением продукта и развертывания. Команда приложения отвечает за нужные ресурсы, инфраструктура отвечает за доставку ответа, каждый поставщик отвечает за собственный сервис. Отчет о нарушении показывает браузерное решение по запросу и политике. Он не доказывает атаку, безопасность приложения при отсутствии отчетов или завершение бизнес-операции. Эти границы не позволяют принять полезный браузерный контроль за универсальную гарантию.
Эта статья помогает спланировать проектирование, ответственность, режим Report-Only, детерминированные проверки, совместимость и откат. Примеры используют синтетические страницы и ограниченные записи результата. Они не описывают ослабление политики ради недоверенного содержимого, сбор учетных данных, обход контроля браузера или изменение чужого сервиса. О других заголовках ответа см. руководство по пользовательским HTTP-заголовкам, а о свидетельствах для релизов руководство по проверке версий браузера.
Определите назначение и границы
Начните с пользовательских сценариев, которые должна защищать политика, и необходимых им ресурсов. Составьте перечень сценариев, стилей, шрифтов, изображений, медиа, фреймов, Workers, WebSocket, адресов назначения fetch, форм и переходов. Включите ресурсы менеджеров тегов, платежей, аналитики, поддержки и Service Workers. Это реестр владельцев, а не копия журнала браузера. Для каждого элемента укажите назначение, владельца, чувствительность данных, среду и предполагаемый срок использования.
Разделяйте границу приложения и границу исполнения в браузере. script-src может ограничить загрузку сценария, но не решает, разрешен ли вызов API, и не дает сценарию серверных привилегий. connect-src ограничивает соединения браузера, но не заменяет контроль доступа конечной точки. Директива фреймов может ограничивать встраивание документа, но не проверяет код или политику дочернего документа. Зафиксируйте эти различия до выбора значений.
Выбирайте наиболее узкую политику, соответствующую контролируемому контракту приложения. Предпочитайте явные источники и стабильные пути; не используйте широкие маски, если достаточно меньшего списка. Проверяйте схемы, порты, перенаправления и поддомены: выражение, кажущееся собственным, может охватывать лишние хосты. Считайте внешний источник зависимостью с владельцем и пересмотром. Не добавляйте его лишь для подавления нарушения, если назначение неизвестно.
Определите критерий успеха для каждого сценария. Например: «интерфейс отрисован, утвержденный модуль загружен, синтетический запрещенный ресурс заблокирован». Это не означает «отчетов не было, значит приложение безопасно». Записывайте ожидаемый видимый результат, политику окончательного ответа и необходимое подтверждение со стороны приложения. Не сохраняйте секреты, данные клиентов, полные тела запросов и не относящиеся к делу атрибуты браузера.
| Вопрос о политике | Что проверить | Ответственный и решение |
|---|---|---|
| Какой исполняемый код предусмотрен? | Итоговые URL, встроенный код, артефакт развертывания | Владелец приложения утверждает nonce, хеш или явный источник |
| Какие сетевые назначения нужны? | Назначения fetch, WebSocket, Worker и формы | Приложение и сервис документируют точные источники |
| Каким встроенным документам доверяют? | URL фреймов, перенаправления, владелец дочернего документа | Продукт утверждает связь и запасной вариант |
| Что отправлять в отчеты? | Настройка report-to или report-uri, срок хранения | Безопасность задает ограничения приватности и эксплуатации |
Выбирайте директивы, не ослабляя доверие
Политика должна выражать модель доверия, а не набор временных исправлений. default-src задает основу для типов без более конкретной директивы. script-src, style-src, img-src, font-src, connect-src, frame-src, worker-src, media-src, object-src, base-uri, form-action и frame-ancestors управляют разными решениями браузера. Поддержка и резервное поведение различаются между версиями, поэтому проверяйте браузеры, поддерживаемые продуктом.
Для исполняемого кода явно задайте разрешенные элементы. Nonce является значением конкретного ответа, которое сервер добавляет к разрешенным встроенным элементам и политике. Дайджест связывает известный блок с точным значением целостности. Внешнему сценарию также нужен разрешенный источник и управление зависимостью. Не считайте nonce паролем и не повторно используйте его между несвязанными ответами. Он разрешает элемент в одном ответе, а не API, пользователя или учетную запись поставщика.
Отдельно рассмотрите разрешение встроенных сценариев и стилей. Широкое исключение может упростить внедрение, но снизить защиту. Если этого требует устаревший фреймворк, задокументируйте зависимость, проверьте путь миграции и ограничьте исключение по времени и области. Не добавляйте unsafe-eval, unsafe-inline, маски или весь домен поставщика только потому, что неудобно разбирать нарушение. Для исключения нужны причина, владелец, дата пересмотра и наблюдаемый запасной путь.
Другие директивы защищают иные границы. base-uri мешает неожиданному базовому URL менять относительную адресацию; form-action ограничивает отправку форм; frame-ancestors задает, кто может встраивать документ, и отличается от frame-src; object-src 'none' часто подходит, если старые плагины не нужны. upgrade-insecure-requests меняет обработку URL, но не исправляет все смешанные интеграции. До включения каждой директивы определите ожидаемый результат.
Не смешивайте CSP с другими заголовками. Политика разрешений управляет делегированием функций, COOP и COEP управляют отношениями контекстов и встраиваемых ресурсов, транспортные заголовки защищают от иных угроз. CSP дополняет их, но не предоставляет их гарантии. В руководстве по безопасным контекстам объясняется, что состояние безопасности браузера также отличается от политики приложения. Назначьте владельца и критерий приемки для каждого контроля.
Начните с наблюдений Report-Only
Сначала используйте Content-Security-Policy-Report-Only на тестовой среде или контролируемом маршруте в production. Поддерживающий браузер оценит кандидатную политику и может сообщить о нарушениях, не блокируя ресурсы. Это помогает обнаружить пропущенные зависимости, но не является подтверждением безопасности. Отчетов может не быть из-за отсутствия поддержки браузером, непроверенного сценария, потери отчета или другого документа. Отсутствие означает «не наблюдалось», а не «безопасно».
Назначьте версию и владельца каждой кандидатной политике. Храните текст политики, маршрут или шаблон ответа, целевые браузеры, проверенный сценарий и дату ревизии. Сводите отчет к источнику документа, классу заблокированного URI, эффективной директиве, типу нарушения, семейству браузера и сценарию. Удаляйте запросы, токены, cookies, текст страницы и другие чувствительные поля. Сборщик отчетов должен быть утвержденным сервисом с контролем доступа и документированным сроком хранения.
Сопоставьте каждый отчет с реестром. Заблокированный URI может быть ожидаемым отказом, отсутствующим собственным ресурсом, внешней зависимостью, неожиданным перенаправлением или значением браузера, не являющимся ресурсом приложения. Проверьте итоговый запрос и ответ в разрешенной среде. Не добавляйте источник автоматически. Выясните владельца, какие данные он получает, поддерживает ли он политику и какой видимый результат будет при отказе загрузки. Отчет доказывает, что браузер оценил политику, но не надежность источника.
Запускайте позитивные и негативные проверки. Позитивный сценарий загружает все обещанные ресурсы. Негативный fixture запрашивает синтетический ресурс, который должен блокироваться, и проверяет видимый запасной вариант. Проверяйте и заголовок, и поведение приложения, чтобы успешная проверка не означала, что запрос вообще не выполнялся. Используйте контролируемый командой тестовый источник и сохраняйте только краткий результат и статус очистки.
| Этап | Поведение браузера | Сохраняемое свидетельство | Условие перехода |
|---|---|---|---|
| Реестр | Кандидатной политики еще нет | Владельцы ресурсов и сценарии | У каждой нужной зависимости есть владелец |
| Report-Only | Сообщает, но загружает ресурсы | Версия политики, очищенные отчеты, результаты | Каждый отчет классифицирован или принят как явное исключение |
| Тестовая среда с блокировкой | Может блокировать ресурсы | Итоговый заголовок, видимый результат, матрица совместимости | Основные сценарии проходят в поддерживаемых браузерах |
| Production с блокировкой | Блокирует вне контракта | Запись развертывания, мониторинг, артефакт отката | Владелец принимает остаточный риск совместимости |
Проверьте детерминированный заблокированный ресурс
Fixture использует собственную тестовую страницу и намеренно блокируемое изображение. Он проверяет видимый запасной вариант, не сохраняет личные данные и удаляет временный узел при успехе и ошибке. Отдавайте страницу тем же путем заголовков, что и кандидатное приложение. Ожидаемое значение задается конфигурацией теста, а не выводится из наблюдения. Это браузерное наблюдение; оно не доказывает, что сервер отверг запрос, атака предотвращена или бизнес-операция завершилась.
async function checkCspFallback(page, expectedBlocked) {
const result = await page.evaluate(async expected => {
const host = document.createElement('section');
const image = document.createElement('img');
const status = document.createElement('output');
let violation = false;
host.dataset.test = 'owned-csp-blocked-image';
status.setAttribute('aria-live', 'polite');
status.textContent = 'Ожидание результата политики';
image.alt = 'Собственный ресурс fixture';
const outcome = new Promise(resolve => {
document.addEventListener(
'securitypolicyviolation',
event => {
if (event.blockedURI.endsWith('/fixtures/csp-owned-image.png')) {
violation = true;
resolve('blocked');
}
},
{ once: true }
);
image.addEventListener('load', () => resolve('loaded'), { once: true });
image.addEventListener('error', () => resolve('error'), { once: true });
});
image.src = '/fixtures/csp-owned-image.png';
host.append(image, status);
document.body.append(host);
let timeoutId;
const timeout = new Promise(resolve => {
timeoutId = window.setTimeout(() => resolve('not-observed'), 5000);
});
const observed = await Promise.race([outcome, timeout]);
window.clearTimeout(timeoutId);
const blocked = violation;
status.textContent = blocked
? 'Запасной вариант: изображение заблокировано политикой приложения'
: observed === 'loaded'
? 'Контроль: собственный ресурс загружен'
: 'Сбой fixture без нарушения политики';
const passed = expected
? observed !== 'error' && observed !== 'not-observed' && violation
: observed === 'loaded' && !violation;
return { marker: host.dataset.test, observed, blocked, passed };
}, expectedBlocked);
try {
if (!result.passed) throw new Error(`Unexpected CSP fixture result: ${result.observed}`);
return result;
} finally {
await page.evaluate(() => document.querySelector('[data-test="owned-csp-blocked-image"]')?.remove());
}
}
Отдавайте /fixtures/csp-owned-image.png из собственной тестовой заглушки как небольшой корректный ответ изображения. Контрольная политика разрешает img-src 'self', поэтому изображение должно загрузиться; кандидатная использует img-src 'none', и браузер отправляет securitypolicyviolation, после чего фиксируется запасной вариант. Если ожидаемое событие не появилось, запишите «не наблюдалось» и проверьте политику, поддержку и fixture. Ошибку сети или политики нельзя считать успехом. После детерминированной ошибки контекст и каталог артефактов должны быть очищены.
Запускайте fixture сначала с контрольной политикой, затем с кандидатной, передавая false и true. Храните записи раздельно. Результат «заблокировано» полезен только если контроль подтверждает доступность собственной заглушки, а кандидатная политика сообщает о нарушении. Записывайте конечный URL, версию политики, версию браузера, маркер, результат и очистку. Не сохраняйте cookies, заголовки авторизации, полные журналы или скопированный production-ответ.
Если настройки, проверка и очистка могут завершиться ошибкой, сохраняйте первой ошибку основного сценария, а очистку сообщайте отдельно. Закрывайте страницу и контекст в завершающем блоке. Повторный запуск должен создавать новый контекст и новую запись. Последующий успех не снимает неопределенность предыдущего запроса, особенно в Report-Only, когда ресурс мог попасть в настоящий сервис. Используйте собственный синтетический источник, чтобы исключить удаленные изменения.
Проверьте совместимость и ответственность
Проверяйте поддерживаемые командой браузеры, ОС, режимы документа и маршруты приложения. Включайте перенаправления, кеш, Service Workers, модульные сценарии, Workers, фреймы, формы и динамические импорты, если они используются. Наличие заголовка в конфигурации CDN не доказывает, что его получил браузер. Проверьте итоговый ответ после перенаправлений и документ, владеющий политикой. У вложенного документа могут быть собственная политика и решения о ресурсах.
Зафиксируйте контракты. Команда приложения определяет необходимые ресурсы и интерфейс альтернативы; хостинг или CDN подтверждает заголовок для каждого канонического маршрута; безопасность задает отчеты, хранение и границы инцидентов; поставщик подтверждает источники, перенаправления и обработку данных; владелец релиза разрешает конфликты и записывает принятую версию. Если зависимость необязательна, опишите альтернативу; если обязательна, не продвигайте политику без совместимого контракта.
Проверьте взаимодействие с кешем и шаблонами. Nonce конкретного ответа нельзя повторно использовать для других ответов; кешированный HTML должен содержать nonce, совпадающий со своей политикой. Сборщик отчетов не должен раскрывать чувствительные значения запроса. Служебный работник страницы может отдавать старую версию, поэтому проверяйте чистый и повторный контекст, если приложение использует такой механизм. Изоляция состояния помогает повторять сценарий, но не удаляет серверные записи и не исправляет устаревшее развертывание.
Рассматривайте изменения политики как изменения кода и инфраструктуры. Сравнивайте директивы, источники, генерацию nonce или хеша, маршрут ответа, получателя отчетов и формат результата. У каждого нового источника должен быть владелец. Назначайте исключениям срок пересмотра. Удаляйте источник только после позитивного сценария, подтверждающего его ненужность, и утвержденного развертывания. Тихий поток отчетов без проверенного сценария не доказывает удаление зависимости.
| Случай совместимости | Проверка | Действие при сбое |
|---|---|---|
| Встроенная инициализация | Nonce или хеш соответствует ответу | Исправить генерацию или перейти на собственный внешний модуль |
| Импорт модуля или Worker | Итоговый URL и применимая директива | Добавить точный собственный источник или применить резервный вариант |
| Фрейм или форма | Источник дочернего документа, перенаправления, frame-src, frame-ancestors, form-action | Согласовать с владельцем, не добавлять глобальную маску |
| Кеш или служебный работник страницы | Версии политики и ресурса совпадают | Сбросить утвержденным способом или дождаться контролируемого истечения |
Наблюдайте, откатывайте и поддерживайте
Сначала включите блокировку на ограниченной группе маршрутов или релизе. Наблюдайте заблокированные ресурсы по версии политики, маршруту, семейству браузера и зависимости. Сводите только необходимое, не сохраняя данные пользователей. Рост может указывать на новую зависимость, рассинхронизацию кеша, смену перенаправления или браузера либо проблему сборщика отчетов. Прежде чем менять политику, сравните с известным позитивным сценарием. Число отчетов не является универсальным показателем безопасности или доступности.
Подготовьте откат заранее. Сохраните предыдущие политику, версию приложения и результаты проверок. Если нарушен обязательный сценарий, совместно восстановите известную политику и приложение, затем повторите позитивную и негативную проверки. Если код стал зависеть от нового ресурса, защитите его runtime-альтернативой или выпустите совместимую версию. Откат не должен удалять защиту транспорта, авторизацию или необходимые диагностические журналы.
После отката сохраните отчеты кандидата как исторические. Не удаляйте свидетельства и не выдавайте Report-Only за примененную политику. Создайте ограниченную задачу для владельца ресурса, политики или релиза. Следующий кандидат должен менять одно известное условие и повторять тот же fixture, чтобы различить источник, nonce, перенаправление, кеш и альтернативу приложения.
Поддерживайте запись политики: текущий заголовок и маршруты, версия, владельцы источников, получатель отчетов, срок хранения, диапазон браузеров, исключения и дата пересмотра. Повторяйте проверку после обновления фреймворка, правил CDN, интеграции поставщика, появления нового фрейма или Worker либо смены канонического хоста. CSP является частью контракта развертывания, поэтому устаревшая документация тоже создает риск.
Что может проверить BotBrowser
Контролируемые контексты BotBrowser позволяют выполнить разрешенный сценарий приложения с чистым или объявленным состоянием браузера, увидеть блокировку синтетического ресурса и сравнить видимую альтернативу в повторяемых запусках. В документации BotBrowser об изоляции нескольких учетных записей описаны отдельные BrowserContext и границы состояния браузера. Это помогает проверить наблюдаемую часть CSP: итоговый URL, доступные тесту метаданные политики, факт загрузки или блокировки и запись очистки.
BotBrowser не создает и не развертывает заголовки, не выбирает безопасный список источников для приложения, не проверяет серверную авторизацию и цепочку поставки зависимостей и не доказывает отсутствие уязвимостей внедрения. Он не делает доверенным чужой ресурс, не предоставляет исключение поставщику и не гарантирует одинаковую поддержку директив во всех браузерах. Также он не доказывает создание записи, прием платежа, отзыв серверного сеанса или отсутствие инцидента. Для этого нужны подтверждения приложения, сервиса, инфраструктуры и безопасности.
BotBrowser может выполнить разрешенный сценарий и наблюдать блокировку синтетического ресурса в изолированном контексте, но не создает и не развертывает политику, не проверяет серверную авторизацию и не доказывает результат безопасности. Ограничьте его запись сценарием, версией политики, конечным URL, сборкой браузера, видимым результатом, классификацией синтетического маршрута и статусом очистки. Не добавляйте туда данные клиента, учетные данные, cookies или полную нагрузку отчета. Помечайте результат как «наблюдение политики браузера» и отдельно указывайте нужное подтверждение бизнес-операции. Чистый контекст является условием запуска, а не способом удалить серверные данные и не сертификатом безопасности.
До релиза владельцы должны подтвердить четыре ограничения: отчет не доказывает атаку; отсутствие отчета не доказывает безопасность; блокировка ресурса не доказывает отказ или удаление на сервере; наблюдение через BotBrowser не заменяет авторизацию и проверку зависимостей приложения. Зафиксируйте политику, совместимость, альтернативу, ответственного за мониторинг и артефакт отката. Это помогает улучшать защиту, не ослабляя ее ради неизвестной зависимости.
Повторяйте те же пользовательские сценарии после изменения политики или собственной зависимости. Узкое сравнение с указанием версии помогает определить, изменилось ли решение браузера, запасной путь приложения или результат отдельного сервиса.
BotBrowser Team
Источники
Похожие статьи
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.