CSP - Content Security Policy
Content Security Policy (CSP) ist ein HTTP-Header der dem Browser vorschreibt welche Quellen für Skripte, Stylesheets, Bilder und andere Ressourcen erlaubt sind. CSP verhindert XSS-Angriffe indem Inline-Skripte und unbekannte externe Quellen blockiert werden. Häufige Fehlkonfigurationen: unsafe-inline, unsafe-eval, Wildcard-Hosts, fehlende default-src Direktive. Empfohlen: Nonce-basiertes CSP oder Strict-Dynamic.
Content Security Policy (CSP) ist ein Browser-Sicherheitsmechanismus der über den HTTP-Response-Header Content-Security-Policy gesteuert wird. Er ermöglicht Webseitenbetreibern präzise zu definieren welche Ressourcen (Skripte, Stylesheets, Bilder, Frames) aus welchen Quellen geladen werden dürfen. Korrekt implementiert ist CSP die wirkungsvollste Defense-in-Depth-Maßnahme gegen Cross-Site-Scripting (XSS).
CSP-Grundstruktur und Direktiven
Content-Security-Policy: <direktive> <quellen>; <direktive> <quellen>; ...
Wichtige Direktiven
| Direktive | Funktion |
|---|---|
default-src | Fallback für alle Ressourcen-Typen |
script-src | JavaScript-Quellen |
style-src | CSS-Quellen |
img-src | Bild-Quellen |
connect-src | Fetch/XHR/WebSocket-Verbindungen |
font-src | Web-Font-Quellen |
frame-src | iframe-Quellen (auch: frame-ancestors gegen Clickjacking) |
object-src | Plugin-Quellen (Flash, Java) - am besten: 'none' |
media-src | Audio/Video-Quellen |
base-uri | Erlaubte base-href-Werte (Base-Tag-Injection!) |
form-action | Erlaubte Formular-Ziele |
Quell-Angaben
| Quell-Angabe | Bedeutung |
|---|---|
'none' | Nichts erlaubt |
'self' | Gleiche Origin |
https: | Alle HTTPS-Quellen |
https://cdn.example.com | Spezifische Domain |
'nonce-<random>' | Einmaliger Zufallswert (sicherste Methode!) |
'hash-<alg>-<base64>' | Erlaubt spezifischen Inline-Code per Hash |
'strict-dynamic' | Vertraut Skripten die von vertrauenswürdigen Skripten geladen werden |
'unsafe-inline' | Alle Inline-Skripte erlaubt (UNSICHER!) |
'unsafe-eval' | Dynamische Code-Ausführung erlaubt (UNSICHER!) |
Häufige CSP-Fehlkonfigurationen
Fehlkonfiguration 1 - unsafe-inline + script-src
Content-Security-Policy: script-src 'self' 'unsafe-inline';
'unsafe-inline'macht CSP WERTLOS gegen XSS!- Alle Inline-Skripte (
<script>alert(1)</script>) erlaubt - Angreifer kann XSS trotz CSP ausführen!
Häufige Begründung: “Wir brauchen Inline-Skripte für Analytics/Tracking” - Lösung: Nonce-basiertes CSP!
Fehlkonfiguration 2 - Wildcard-Hosts
Content-Security-Policy: script-src 'self' https: *.cdn.com;
https:erlaubt ALLE HTTPS-Quellen weltweit → wertlos!*.cdn.com: Angreifer legt Datei auf cdn.com ab und umgeht CSP
Bypass-Beispiel:
CSP: script-src 'self' *.github.io;
→ Angreifer erstellt: attacker.github.io/payload.js
→ Injiziert: <script src="https://attacker.github.io/payload.js"></script>
→ CSP erlaubt es (github.io im Wildcard)!
Fehlkonfiguration 3 - JSONP-Endpoint in Whitelist
CSP: script-src 'self' https://api.google.com;
- Öffentlicher JSONP-Endpoint von google.com → CSP erlaubt es!
- Callback-Parameter wird ausgeführt
- Bekannte JSONP-Bypass-Endpoints existieren bei großen CDNs
Fehlkonfiguration 4 - Fehlende Direktiven
Content-Security-Policy: script-src 'self';
- Ohne
default-src: andere Ressourcen (img-src, style-src) unkontrolliert! - CSS-Injection möglich: style-src nicht gesetzt
Fehlkonfiguration 5 - object-src fehlt
Content-Security-Policy: script-src 'self'; default-src 'self';
- Ohne
object-src 'none': Browser-Plugins könnten geladen werden object-src 'none'IMMER explizit setzen!
Fehlkonfiguration 6 - base-uri fehlt
Content-Security-Policy: script-src 'self';
- Ohne
base-uri 'self': Base-Tag-Injection möglich <base href="https://evil.com/">→ alle relativen URLs zeigen auf evil.com!
Nonce-basiertes CSP (Empfohlen)
Funktionsprinzip
- Server generiert zufälligen Nonce pro Request (min. 128 Bit!)
- Nonce in CSP-Header und im erlaubten Script-Tag setzen
- Browser führt nur Script-Tags mit korrektem Nonce aus
- XSS-Payload hat keinen Nonce → blockiert!
Python (Flask)
import secrets
from functools import wraps
def with_csp(f):
@wraps(f)
def wrapper(*args, **kwargs):
nonce = secrets.token_urlsafe(32) # 256-Bit Nonce!
g.csp_nonce = nonce
response = f(*args, **kwargs)
response.headers['Content-Security-Policy'] = (
f"script-src 'nonce-{nonce}' 'strict-dynamic'; "
"object-src 'none'; base-uri 'self';"
)
return response
return wrapper
Node.js (Express)
const crypto = require('crypto');
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(32).toString('base64');
res.setHeader('Content-Security-Policy',
`script-src 'nonce-${res.locals.nonce}' 'strict-dynamic'; ` +
`object-src 'none'; base-uri 'self';`);
next();
});
Nonce-Sicherheitsregeln
- Nonce NIEMALS wiederverwenden! (Pro Request neu generieren)
- Nonce NICHT in URLs, Logs oder Quellcode hardcoden
- Nonce min. 128 Bit (22+ Base64-Zeichen) - brute-force-safe
'unsafe-inline'ENTFERNEN wenn Nonce aktiv ist (Nonce macht es obsolet)'strict-dynamic'+ Nonce: dynamisch geladene Skripte erben Vertrauen
CSP Strict-Dynamic Mode
Zweck
'strict-dynamic' erlaubt dynamisch geladene Skripte von vertrauenswürdigen Skripten.
Ohne strict-dynamic: Vertrauenswürdiges Skript lädt dynamisch widget.js → widget.js nicht in Whitelist → BLOCKIERT! Viele Frontend-Frameworks scheitern an normalem CSP!
Mit 'strict-dynamic':
Content-Security-Policy: script-src 'nonce-ABC' 'strict-dynamic';
- Nonce-Skript darf weitere Skripte dynamisch laden
- Geladene Skripte erben das Vertrauen des Parent-Skripts
- Kein Whitelisting externer Hosts mehr nötig!
Empfohlenes CSP-Template (2026 State-of-the-Art)
Content-Security-Policy:
default-src 'none';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
style-src 'self' 'nonce-{RANDOM}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests;
frame-ancestors 'none': verhindert Clickjacking!upgrade-insecure-requests: HTTP→HTTPS automatisch
CSP Testing und Deployment
1. CSP Evaluator (Google)
- csp-evaluator.withgoogle.com
- CSP-Header eingeben → automatische Schwachstellenanalyse
- Zeigt: welche Direktiven fehlen, welche unsicheren Keywords gesetzt
2. Report-Only Modus (Deployment-Vorbereitung!)
Content-Security-Policy-Report-Only: script-src 'self' 'nonce-XYZ';
report-to: /csp-violations
- Browser blockiert NICHTS (keine Breaking Changes!)
- Sendet Violation-Reports für jede Policy-Verletzung
- Sammeln + Analysieren was gebrockt würde
- Dann: echten CSP-Header schalten
Violation-Report (JSON):
{
"csp-report": {
"document-uri": "https://example.com/page",
"violated-directive": "script-src",
"blocked-uri": "https://evil.com/payload.js",
"original-policy": "script-src 'self'"
}
}
3. Burp Suite CSP-Testing
- Burp Extension: CSP Auditor
- Prüft auf bekannte Bypasses: unsafe-inline, JSONP-Hosts, Wildcards
4. Schrittweise Migration
- Report-Only ohne Blockierung (1-2 Wochen sammeln)
- Echte CSP auf weniger kritischen Seiten
- Auswertung + Anpassung
- Rollout auf alle Seiten
CSP und Single-Page-Applications (SPA)
- React: webpack nonce-Injection konfigurieren
- Next.js:
next.config.jsCSP-Header + Script nonce in_document.tsx - Vue: nonce in
index.htmlüber Server-Variable injiziert - Angular:
angular.jsonsecurity-policy oder Nonce über Server-Header