До основного вмісту

Content Security Policy (CSP): захист від XSS-атак

Безпека · 19.04.2026 · 4 хв читання
Ілюстрація до статті «Content Security Policy (CSP): захист від XSS-атак»

Content Security Policy (CSP) — HTTP-заголовок, який повідомляє браузеру, звідки дозволено завантажувати скрипти, стилі, зображення та інші ресурси. Це ефективний захист від XSS-атак і data injection.

Базові директиви CSP

ДирективаКонтролює
default-srcДжерело за замовчуванням для всіх ресурсів
script-srcJavaScript
style-srcCSS
img-srcЗображення
connect-srcAJAX, WebSocket, Fetch
font-srcШрифти
frame-srciframes
report-uriURL для звітів про порушення

Приклади CSP-політик

Сувора політика (для простих сайтів)

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'

Для WordPress з Google Fonts і GTM

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https://www.google-analytics.com;

Налаштування через .htaccess

<IfModule mod_headers.c>
    Header always set Content-Security-Policy "        default-src 'self';         script-src 'self' 'unsafe-inline' 'unsafe-eval';         style-src 'self' 'unsafe-inline';         img-src 'self' data: https:;         font-src 'self' data:;         connect-src 'self';"
</IfModule>

Режим Report-Only для тестування

# Не блокує, лише надсилає звіти
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report.php
💡 Починайте з Content-Security-Policy-Report-Only — збирайте порушення тиждень, потім переходьте на сувору CSP без поломки сайту.
⚠️ 'unsafe-inline' і 'unsafe-eval' послаблюють захист. Використовуйте nonce або hash для інлайн-скриптів у production.

Розбір повідомлень про порушення CSP

Браузер завжди друкує в консолі, яка саме директива спрацювала і що заблоковано — не потрібно вгадувати.

  • Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. — інлайн-скрипт у HTML заблоковано; рішення — винести код в окремий файл або підписати блок nonce/hash.
  • Refused to load the stylesheet 'https://fonts.googleapis.com/...' because it violates the following Content Security Policy directive: "style-src 'self'". — домен шрифтів не додано до style-src, стилі із зовнішнього CDN не підключаться.
  • Refused to connect to 'https://api.example.com/...' because it violates the following Content Security Policy directive: "connect-src 'self'". — AJAX/fetch-запит до стороннього API заблоковано; домен потрібно явно перелічити в connect-src.
  • Refused to frame 'https://widget.example.com/' because it violates the following Content Security Policy directive: "frame-src 'self'". — вбудований iframe стороннього віджета не відкриється, доки його домен не зʼявиться в frame-src.
  • JSON-звіт на report-uri вигляду {"csp-report": {"document-uri": "...", "violated-directive": "script-src-elem", "blocked-uri": "...", "line-number": 42}} — так браузер повідомляє про порушення на сервер: видно, який рядок коду і який ресурс спричинили блокування, навіть якщо користувач не відкривав консоль.

Способи задати CSP: де і коли

СпосібДе задаєтьсяПідтримка frame-ancestorsКоли використовувати
HTTP-заголовок Content-Security-PolicyВідповідь сервера (Nginx, Apache, PHP)Так, повністюОсновний спосіб у production
Тег <meta http-equiv> в HTMLУсередині <head> сторінкиНі, директива ігнорується в meta-тезіКоли немає доступу до серверних заголовків, наприклад на статичному хостингу без конфігурації
Content-Security-Policy-Report-OnlyОкремий заголовок відповіді сервераТакТестовий режим: збирає порушення, але нічого не блокує
Одночасно enforce і report-onlyДва заголовки одразуТакПоступове посилення чинної політики зі збором даних про нові порушення

Коли вмикати строгий CSP зарано

  • У коді багато інлайн-обробників на кшталт onclick="..." і розкиданих <script> без атрибутів — строгий script-src без 'unsafe-inline' зламає їх усі одразу; потрібен поступовий перехід на nonce чи hash, а не миттєве увімкнення.
  • На сайті стоять сторонні віджети — чат підтримки, платіжна форма, рекламна мережа, — які самі впроваджують інлайн-код і тягнуть ресурси з непередбачуваних доменів; їхні джерела доведеться вручну додавати до політики і періодично оновлювати список.
  • Сайт роздається через кілька CDN із доменами, що часто змінюються — фіксований список доменів у CSP швидко застаріває і потребує постійної ручної синхронізації.
  • Немає можливості збирати звіти про порушення — увімкнення строгої політики без періоду Report-Only майже гарантовано ламає частину функціональності непомітно для адміністратора, доки не поскаржаться користувачі.

Перевірка, що політика справді застосовується

# Переконатися, що заголовок дійсно надходить від сервера
curl -I https://example.com | grep -i content-security-policy

# Відкрити DevTools → Console на бойовій сторінці:
# рядків "Refused to" бути не повинно, якщо політика в режимі enforce

Для перевірки самої політики на логічні помилки (занадто широкі джерела, відсутність object-src) зручно вставити значення заголовка в незалежний онлайн-аналізатор CSP, наприклад CSP Evaluator від Google. Якщо паралельно налаштовано report-uri, звіти про спроби порушення надходять навіть коли політика вже в режимі enforce, а не лише на етапі Report-Only.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці