Content-Security-Policy: unsafe-inline loswerden
Fast jede CSP, die man im Feld antrifft, enthält 'unsafe-inline'. Und fast jede davon ist damit an ihrer wichtigsten Stelle wirkungslos.
Das ist keine Nachlässigkeit einzelner. Es ist der Weg des geringsten Widerstands: Ohne 'unsafe-inline' bricht auf den meisten gewachsenen Seiten sofort etwas, und die Richtlinie wird dann eben so lange aufgeweicht, bis nichts mehr bricht.
Warum das die Schutzwirkung aufhebt#
Der Kern einer CSP für Skripte ist eine Herkunftsprüfung: Der Browser führt nur aus, was aus einer erlaubten Quelle kommt. Cross-Site-Scripting funktioniert, indem ein Angreifer eigenen Code in deine Seite einschleust — und der steht dann inline, direkt im Markup.
script-src 'self' 'unsafe-inline' sagt dem Browser: Führe alles aus, was im Dokument steht. Der eingeschleuste Code steht im Dokument. Er wird ausgeführt.
Man behält also die Richtlinie, den Kopfzeileneintrag und das gute Gefühl — und verliert genau den Schutz, für den man sie eingeführt hat. Wie eine CSP im Zusammenspiel mit den übrigen Kopfzeilen wirkt, steht in Security-Header und SEO.
Die zwei Auswege#
Nonce. Der Server erzeugt bei jedem Aufruf eine Zufallszeichenfolge, schreibt sie in die Kopfzeile (script-src 'nonce-abc123') und in jedes erlaubte <script nonce="abc123">. Nur wer die Zahl kennt, wird ausgeführt — und der Angreifer kennt sie nicht, weil sie sich bei jedem Aufruf ändert.
Hash. Der Server nennt den SHA-Hash des erlaubten Skriptinhalts: script-src 'sha256-...'. Ändert sich das Skript um ein Zeichen, passt der Hash nicht mehr.
Die Wahl ist im Kern eine Frage der Ausspielung:
| Nonce | Hash | |
|---|---|---|
| Braucht | dynamisch erzeugte Antwort | nichts |
| Passt zu | PHP, Node, jedem Server, der die Seite baut | statischen Seiten und Caching |
| Bei Änderung am Skript | nichts zu tun | Hash neu bilden |
| Fällt aus bei | vollständigem Seiten-Cache | Skripten mit wechselndem Inhalt |
Faustregel: Wer eine Seite ausliefert, die vollständig zwischengespeichert wird, kann keine Nonce verwenden — sie wäre in jeder gecachten Kopie dieselbe und damit wertlos. Dann bleibt der Hash.
Der Fallstrick, den fast alle treffen#
Steht in der Richtlinie sowohl eine Nonce oder ein Hash als auch 'unsafe-inline', dann ignorieren moderne Browser 'unsafe-inline' — das ist ausdrücklich so festgelegt und als Rückfall für alte Browser gedacht.
Das klingt gut und hat eine unangenehme Folge: Deine Seite funktioniert im Test weiter, obwohl du die Richtlinie verschärft hast — aber nur, weil der Browser den strengeren Teil anwendet. Wer die Nonce falsch einbaut und den Fehler nicht bemerkt, weil 'unsafe-inline' noch als Netz gedacht war, merkt es erst, wenn er es entfernt.
Deshalb: 'unsafe-inline' wirklich streichen, nicht danebenstehen lassen.
Der Weg dorthin, ohne die Seite abzuschießen#
1. Erst messen, dann erzwingen. Es gibt eine zweite Kopfzeile, die alles meldet und nichts blockiert:
`` Content-Security-Policy-Report-Only: script-src 'self'; report-uri /csp-melden ``
Beide Kopfzeilen dürfen gleichzeitig gesetzt sein — die eine erzwingt die alte, laxe Richtlinie, die andere protokolliert, was die neue brechen würde. Das ist der einzige vernünftige Weg auf einer Seite, die schon läuft.
2. Die Meldungen lesen. In der Regel kommen drei Sorten: eigene Inline-Skripte, Inline-Skripte von Plugins, und fremde Dienste. Die dritte Sorte ist der eigentliche Befund — siehe SEO-Risiken durch Third-Party-Skripte.
3. Aufräumen statt erlauben. Jedes Inline-Skript, das in eine eigene Datei kann, sollte in eine eigene Datei. Das reduziert die Zahl der Nonces und Hashes auf ein Maß, das man pflegen kann.
4. Erst dann umstellen.
Was 'unsafe-inline' bei Styles bedeutet#
Bei style-src ist die Lage entschärft, aber nicht harmlos. Über eingeschleustes CSS lassen sich Inhalte verdecken, Schaltflächen verschieben und in manchen Konstellationen Daten abgreifen. Die Schutzwirkung ist geringer als bei Skripten, der Aufwand für die Umstellung aber auch — viele Seiten kommen bei Styles ohne 'unsafe-inline' aus, sobald die letzten style=-Attribute weg sind.
Reihenfolge: erst script-src, dann style-src. Der Ertrag liegt bei den Skripten.
Und wenn es nicht geht#
Manche Baukästen und Plugins erzeugen Inline-Skripte, an die man nicht herankommt. Dann ist 'unsafe-inline' bei script-src eine bewusste Entscheidung und keine Nachlässigkeit — sie sollte nur so dokumentiert sein, dass beim nächsten Sicherheitscheck niemand denkt, hier sei etwas übersehen worden.
Was deine Seite heute an Kopfzeilen ausliefert, zeigt der Security-Scan.