Chybí Content-Security-Policy
Co to je
Vaše stránky neposílají hlavičku Content-Security-Policy. Tahle hlavička prohlížeči říká, z jakých zdrojů smí načíst skripty, obrázky, fonty, iframy.
Bez ní prohlížeč spustí jakýkoliv kód, který se v HTML objeví — ať už ho tam dal váš tým, nebo útočník přes XSS.
Proč je to problém
CSP je jediná efektivní obrana proti cross-site scripting (XSS). Když útočník někde v aplikaci objeví díru, kterou tam protlačí <script>, bez CSP mu prohlížeč ten script s radostí spustí. S CSP mu prohlížeč řekne „tenhle zdroj nemám v allowlistu“ a script padne.
Druhá rovina — ochrana před injektovanými drainery a SEO spamem. Pokud máte správně nastavené script-src, útočník přes hacknutý plugin nemůže snadno přidat externí script.
Jak to opravit
CSP je politika, která může rozbít stránku. Nasaďte ji nejdříve v report-only režimu.
Fáze 1 — report-only
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; report-uri /csp-reportTahle politika nic neblokuje — jen pošle JSON na /csp-report při každém porušení. Sledujte logy několik dnů, abyste věděli, jaké skripty/obrázky/fonty stránka skutečně používá.
Fáze 2 — vyladit allowlist
Z reportů sestavte konkrétní allowlist. Příklad pro WordPress s Google Fonts a analytikou Umami:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://i.xhs.cz;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://*.gravatar.com;
connect-src 'self' https://i.xhs.cz;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';Fáze 3 — přepnout na enforce
Až report-only nehlásí žádné legitimní porušení, změňte hlavičku z Content-Security-Policy-Report-Only na Content-Security-Policy.
Pozor na 'unsafe-inline'
Inline skripty (<script>...</script>) i inline event handlery (onclick="...") vyžadují 'unsafe-inline'. Tím ale CSP přijde o většinu hodnoty proti XSS. Lepší je použít nonce nebo hash a inline skripty postupně vyhodit. WordPress má pro nonce vestavěnou podporu.
Konkrétní implementace
- nginx:
add_header Content-Security-Policy "..." always; - Apache:
Header always set Content-Security-Policy "..." - WordPress plugin: HTTP Headers