Eine sauber gebaute Content Security Policy verwandelt die meisten Script-Injection-Lücken in bloße Logzeilen. Die Schwierigkeit liegt darin, sie auszurollen, ohne die Website zu zerlegen — das verlangt ein schrittweises Vorgehen statt eines aus einem generischen Beispiel kopierten Headers. Dieser Leitfaden behandelt die Direktiven, auf die es ankommt, den Weg über den Beobachtungsmodus vor dem Blockieren, und die typischen Fallen von Seiten mit vielen Drittanbieter-Skripten.
Wozu eine CSP tatsächlich dient
Die Content Security Policy ist ein HTTP-Header, mit dem ein Server dem Browser mitteilt, welche Inhaltsquellen für eine Seite legitim sind. Der Browser setzt diese Liste anschließend durch: Ein Skript von einer nicht erlaubten Domain, ein per Angriff eingeschleuster Inline-Eventhandler oder ein Netzwerkaufruf an einen feindlichen Server werden vor der Ausführung blockiert.
Der Hauptnutzen betrifft Angriffe durch Script Injection, allgemein als XSS bekannt. Selbst wenn es einem Angreifer gelingt, ein <script>-Tag in eine Seite einzuschleusen, verhindert eine strikte Richtlinie dessen Ausführung, weil es keiner deklarierten Quelle entspricht. Die CSP deckt zudem den Schutz gegen die Einbettung in ein fremdes iframe ab — über die Direktive frame-ancestors, die den alten Header X-Frame-Options ablöst.
Die Direktiven, die eine Richtlinie strukturieren
Eine Richtlinie liest sich als Folge von Direktiven, durch Semikolons getrennt, die jeweils einen Ressourcentyp und dessen erlaubte Quellen benennen. Die Direktive default-src dient als Auffangnetz: Sie greift für jeden Typ, den keine speziellere Direktive abdeckt.
| Direktive | Steuert | Sinnvoller Startwert |
|---|---|---|
default-src | Auffangnetz für nicht genannte Typen | 'self' |
script-src | Herkunft der JavaScript-Dateien | 'self' + Nonce |
style-src | Herkunft der Stylesheets | 'self' |
img-src | Herkunft der Bilder | 'self' data: |
connect-src | Ziele von fetch-, XHR- und WebSocket-Aufrufen | 'self' + Fach-API |
frame-ancestors | Wer die Seite in ein iframe einbetten darf | 'self' oder 'none' |
base-uri | Erlaubte Werte für das base-Tag | 'self' |
form-action | Ziele für das Absenden von Formularen | 'self' |
object-src | Alt-Plugins (Flash, Applets) | 'none' |
Drei Schlüsselwörter tauchen ständig auf. Der Wert 'self' erlaubt dieselbe Herkunft wie die Seite. Der Wert 'none' verbietet jede Quelle. Umgekehrt öffnen 'unsafe-inline' und 'unsafe-eval' die Tür für Inline-Skripte und dynamische Auswertung — und nehmen der Richtlinie einen Großteil ihres Werts, sobald sie script-src betreffen.
Der moderne Ansatz: Nonces und strict-dynamic
Eine Liste vertrauenswürdiger Domains in script-src funktioniert, altert aber schlecht: Jeder neue Drittdienst verlängert die Liste, und eine einzige kompromittierte erlaubte Domain genügt, um die Richtlinie auszuhebeln. Der heute empfohlene Ansatz stützt sich auf Nonces — zufällige Token, die pro Antwort erzeugt und an die legitimen Skripte gebunden werden.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m2026' 'strict-dynamic';
style-src 'self';
img-src 'self' data:;
connect-src 'self' https://api.beispiel.de;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests;Auf Seitenebene trägt jedes vertrauenswürdige Skript das passende Attribut, mit einem Token, das bei jeder Anfrage neu erzeugt und nie wiederverwendet wird.
<script nonce="r4nd0m2026" src="/assets/app.js"></script>Das Schlüsselwort 'strict-dynamic' vervollständigt den Aufbau: Ein per Nonce bereits erlaubtes Skript darf seinerseits weitere Skripte laden, ohne dass jede Abhängigkeit gelistet sein muss. Die Richtlinie konzentriert sich dann auf die Vertrauenswurzel statt auf eine Domainliste, die mit der Zeit ausfranst.
Eine Liste erlaubter Domains wächst mit jeder Integration; ein Nonce vertraut nur dem Code, den der Server tatsächlich ausgeliefert hat.
Ausrollen, ohne die Website zu zerlegen
Das Risiko einer CSP ist nicht die Lücke, sondern die Regression: Eine etwas zu strikte Direktive lässt eine Karte, ein Zahlungsformular oder ein Analyse-Tag verschwinden. Das Gegenmittel ist ein eigener Header, Content-Security-Policy-Report-Only, der exakt dieselben Regeln anwendet, ohne zu blockieren. Der Browser meldet lediglich, was er abgelehnt hätte.
Methode in drei Schritten. Die Richtlinie zunächst als Report-Only veröffentlichen und Verstöße über mehrere Tage echten Verkehrs sammeln. Danach die auftauchenden legitimen Quellen korrigieren (Widgets, Schriften, APIs). Zuletzt auf den blockierenden Header umschalten, sobald der Meldestrom verstummt ist.
Die Meldungen laufen über die Direktive report-to, gestützt auf die Reporting API, die bei jedem Verstoß ein JSON-Dokument an einen Endpunkt sendet. Für breitere Kompatibilität mit älteren Browsern wird die alte Direktive report-uri während des Übergangs oft parallel gepflegt.
Die Fallen von Seiten mit vielen Drittanbietern
Eine Visitenkarten-Website auf einem selbst kontrollierten Server eignet sich gut für Nonces. Eine Website auf einem erweiterbaren CMS wirft ein anderes Problem auf: Zahlreiche Plugins schleusen beim Rendern eigene Inline-<script>-Tags ohne Nonce ein. Bei WordPress etwa läuft die Versuchung, 'unsafe-inline' wiederherzustellen, um die Fehler zum Schweigen zu bringen, darauf hinaus, den Schutz genau an der empfindlichsten Stelle abzuschalten.
Drei Reflexe begrenzen den Schaden. Erstens: die tatsächlich genutzten Drittanbieter-Skripte inventarisieren und die entfernen, die keinen Zweck mehr erfüllen. Zweitens: Erweiterungen bevorzugen, die das Anhängen eines Nonce erlauben, oder Inline-Skripte in Dateien derselben Herkunft auslagern. Drittens: connect-src mit derselben Sorgfalt behandeln wie script-src — es ist die Direktive, die den Abfluss von Daten an einen fremden Server verhindert, meist das eigentliche Ziel einer gelungenen Injection.
Die Richtlinie prüfen und über die Zeit pflegen
Eine CSP ist nie fertig: Jede neue Funktion, jede Marketing-Integration, jedes Plugin-Update kann eine legitime Quelle einführen, die die Richtlinie nicht kennt. Ohne Pflegedisziplin drohen zwei Abdriften. Bei der ersten schwillt die Liste erlaubter Quellen unter dringenden Anfragen an, bis so viel erlaubt ist, dass der Schutz seinen Sinn verliert. Bei der zweiten blockiert die Richtlinie stillschweigend eine Funktion, die niemand testet — bis ein Nutzer die Störung meldet.
Die Browser-Konsole bleibt das erste Diagnosewerkzeug: Jeder Verstoß erscheint dort mit der auslösenden Direktive und der abgelehnten Ressource, was genügt, um eine Regression während der Entwicklung zu erkennen. Für eine systematischere Bewertung benoten Online-Analysewerkzeuge eine Richtlinie und weisen auf die Schlüsselwörter hin, die sie schwächen — allen voran 'unsafe-inline' und zu weite Platzhalter. Diese Prüfung in die Continuous Integration einzubauen verhindert, dass eine Richtlinie unbemerkt verfällt.
Der Pflege hilft ein knapper Regelsatz: den Grund für jede erlaubte Quelle dokumentieren, die Richtlinie bei jedem neuen Drittdienst überprüfen und den Report-Endpunkt auch nach dem Wechsel in den Blockiermodus aktiv lassen. Verstöße, die in der Produktion weiter eintreffen, deuten entweder auf einen laufenden Angriff hin oder auf eine beim Härten vergessene legitime Funktion. In beiden Fällen verdient die Information, gesehen zu werden.
Das Wichtigste
- Die CSP neutralisiert die Ausführung eingeschleuster Skripte — vorausgesetzt,
'unsafe-inline'bleibt ausscript-srcheraus. - Nonces zusammen mit
'strict-dynamic'sind den Domainlisten deutlich überlegen. - Der Rollout läuft erst über
Report-Only, dann blockierend, sobald die Fehlalarme abgearbeitet sind. frame-ancestors,base-uri,form-actionundobject-src 'none'erweitern den Schutz über Skripte hinaus.
Als ich zum ersten Mal eine CSP direkt im Blockiermodus ausgerollt habe, habe ich mitten am Tag den Bezahlvorgang eines Kunden zerlegt. Seither starte ich ausschließlich mit Report-Only und lasse eine ganze Woche laufen, bevor ich anziehe. Bei WordPress-Seiten voller Erweiterungen halte ich eine halb strenge, aber tatsächlich aktive CSP für mehr wert als eine auf dem Papier perfekte Richtlinie, die niemand einzuschalten wagt. — Simon Janvier
Zum Weiterlesen
Referenz der Direktiven und ihrer Unterstützung: Content-Security-Policy bei MDN.
