Sicherheits-Ratgeber
Eine Content Security Policy wird selten absichtlich entfernt. Sie wird für eine schnelle Lösung gelockert, zum Debuggen auf Report-Only gestellt oder von einem Plugin oder einer CDN-Regel ersetzt, und die Website funktioniert weiter. Dieser Ratgeber zeigt, wie Sie es bemerken: ein Header-Diff für Ihre CI, die Verstoßmeldungen, die der Browser Ihnen schickt, und was ein Scan-Vergleich zeigen kann und was nicht.
01 Warum es wichtig ist
Die Richtlinie entsteht oft an mehr als einer Stelle. Die Anwendung setzt sie, ein CMS-Plugin ergänzt eine eigene, eine CDN- oder WAF-Regel schreibt Header um oder hängt welche an, und die Hosting-Oberfläche hat auch noch ein Feld dafür. Jede davon kann sie ändern, ohne dass die Änderung je durch das Repository läuft, in dem Ihre Richtlinie geprüft wird.
Manche Änderungen machen die Richtlinie strenger, ohne dass es jemand merkt: kommen zwei Content-Security-Policy-Header an, erzwingt der Browser beide, eine Ressource muss also jede davon bestehen. Die gefährlichen Änderungen gehen in die andere Richtung: eine neue Wildcard, ein hinzugefügtes unsafe-inline oder der Wechsel vom erzwingenden Header auf Report-Only. Nach so einer Änderung sieht die Seite genauso aus wie vorher, sie ist nur nicht mehr geschützt.
02 Ein Beispiel
Ein typischer Fall: ein Zahlungs-Widget auf der Kasse lädt nach einem Update des Anbieters nicht mehr. Um die Ursache schnell zu finden, wird der Header-Name auf Content-Security-Policy-Report-Only geändert, das Widget läuft wieder, und das Zurückstellen wird vergessen. Vorher und nachher zeigt curl Folgendes:
$ curl -sI https://www.example.com/checkout | grep -i '^content-security-policy' content-security-policy: default-src 'self'; script-src 'self' 'nonce-Q2hlY2tvdXQ'; style-src 'self'; img-src 'self' data:; frame-src https://pay.example-psp.com; object-src 'none'; base-uri 'self'; form-action 'self' https://pay.example-psp.com; frame-ancestors 'none'; report-to csp $ curl -sI https://www.example.com/checkout | grep -i '^content-security-policy' content-security-policy-report-only: default-src 'self'; script-src 'self' 'nonce-V2lkZ2V0Zml4'; style-src 'self'; img-src 'self' data:; frame-src https://pay.example-psp.com; object-src 'none'; base-uri 'self'; form-action 'self' https://pay.example-psp.com; frame-ancestors 'none'; report-to csp Der Wert ist bis auf die Nonce derselbe, deshalb übersieht ein kurzer Blick auf den Richtlinientext die Änderung. Geändert hat sich nur der Header-Name und mit ihm die gesamte Wirkung: der Browser meldet jetzt Verstöße und blockiert keinen davon. Genau diese Änderung soll jeder der folgenden Wege finden.
03 Weg 1
Legen Sie die erwartete Richtlinie als Datei in Ihr Repository, ci/csp-expected.txt, in derselben normalisierten Form, die das Skript erzeugt: Header-Name kleingeschrieben, Nonce durch einen Platzhalter ersetzt. Nach jedem Deploy holt das Skript den ausgelieferten Header und vergleicht. Weil diff bei Unterschieden mit einem Status ungleich null endet und set -e das Skript dort abbricht, schlägt der Pipeline-Job fehl.
#!/bin/sh set -eu curl -fsS -o /dev/null -D headers.txt "$1" tr -d '\r' < headers.txt \ | sed -E "s/'nonce-[^']+'/'nonce-X'/g" \ | awk -F': ' 'tolower($1) ~ /^content-security-policy(-report-only)?$/ { print tolower($1) ": " substr($0, length($1) + 3) }' \ | sort > csp-live.txt diff -u ci/csp-expected.txt csp-live.txt Für das Beispiel oben scheitert der Job mit dieser Ausgabe, die die Änderung genau benennt:
--- ci/csp-expected.txt 2026-09-18 10:12:03.000000000 +0200 +++ csp-live.txt 2026-09-25 14:31:47.412803113 +0200 @@ -1 +1 @@ -content-security-policy: default-src 'self'; script-src 'self' 'nonce-X'; style-src 'self'; img-src 'self' data:; frame-src https://pay.example-psp.com; object-src 'none'; base-uri 'self'; form-action 'self' https://pay.example-psp.com; frame-ancestors 'none'; report-to csp +content-security-policy-report-only: default-src 'self'; script-src 'self' 'nonce-X'; style-src 'self'; img-src 'self' data:; frame-src https://pay.example-psp.com; object-src 'none'; base-uri 'self'; form-action 'self' https://pay.example-psp.com; frame-ancestors 'none'; report-to csp Lassen Sie dasselbe Skript zusätzlich regelmäßig laufen, um Änderungen zu finden, die nicht durch Ihre Pipeline kommen, etwa eine CDN-Regel oder ein Plugin-Update. Unser Ratgeber zum Überwachen von Security-Headern enthält eine Cron-Fassung, die den Diff per Mail schickt, statt einen Job scheitern zu lassen.
04 Weg 2
Der Header-Diff sieht, was Ihr Server sendet. Verstoßmeldungen sehen, was in den Browsern Ihrer Besucher passiert. Benennen Sie einen Endpunkt mit Reporting-Endpoints, verweisen Sie mit report-to darauf und behalten Sie report-uri für Browser, die report-to noch nicht unterstützen.
add_header Reporting-Endpoints 'csp="https://www.example.com/csp-reports"' always; add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; report-to csp; report-uri https://www.example.com/csp-reports" always; Jede Meldung enthält zwei Felder, die eine Änderung an der Richtlinie selbst verraten. disposition ist enforce bei einer erzwingenden und report bei einer Report-Only-Richtlinie, ein Strom von enforce-Meldungen, der zu report wechselt, ist also genau der Wechsel aus dem Beispiel oben. originalPolicy enthält den vollständigen Richtlinientext, den der Browser erhalten hat, eine neue Quelle oder eine verlorene Direktive zeigt sich dort also mit dem ersten gemeldeten Verstoß.
[{ "age": 0, "type": "csp-violation", "url": "https://www.example.com/checkout", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...", "body": { "documentURL": "https://www.example.com/checkout", "blockedURL": "https://cdn.example-widget.net/loader.js", "effectiveDirective": "script-src-elem", "disposition": "report", "originalPolicy": "default-src 'self'; script-src 'self' 'nonce-V2lkZ2V0Zml4'; ...; report-to csp", "referrer": "", "sample": "", "statusCode": 200 } }] Meldungen über report-to kommen gesammelt als application/reports+json, Meldungen über report-uri als application/csp-report mit denselben Angaben in einem csp-report-Objekt mit Bindestrich-Feldnamen. Meldungen kommen nur, wenn etwas gegen die Richtlinie verstößt, sie ergänzen den Header-Diff also, statt ihn zu ersetzen: eine Richtlinie, die so weit gelockert wird, dass nichts mehr verstößt, wird still, statt Alarm zu schlagen.
05 Weg 3
Unser Scan-Vergleich stellt zwei Scans derselben Website nebeneinander und zeigt, welche Befunde behoben sind, welche neu dazugekommen sind und wie sich die Bewertungen bewegt haben. Er vergleicht Befunde, nicht den Header-Text. Eine Änderung an der Richtlinie erscheint dort nur, wenn sie einen Befund erzeugt oder beseitigt.
Das deckt die wichtigsten Änderungen ab. Der Wechsel auf Report-Only aus dem Beispiel erscheint als neuer Befund Content-Security-Policy wird nur im Report-Only-Modus ausgeliefert. Eine entfernte Richtlinie erscheint als Content-Security-Policy fehlt, und eine saubere Richtlinie, die unsafe-inline oder eine Wildcard dazubekommt oder form-action verliert, als Content-Security-Policy ist vorhanden, aber schwach.
Unsichtbar bleiben ein neuer Host in einer weiterhin starken Richtlinie und eine weitere Schwäche in einer Richtlinie, die bereits als schwach gemeldet ist, weil der Befund vorher wie nachher existiert. Nutzen Sie den Scan als Alarm, wenn Ihre Richtlinie schlechter wird, nicht als Diff. Das Monitoring im Agency-Tarif fährt den Vergleich regelmäßig und schickt Ihnen eine Mail, wenn ein Befund auftaucht.
06 Fragen
Vergleichen Sie den ausgelieferten Header mit der erwarteten Richtlinie. Holen Sie ihn per curl, ersetzen Sie die Nonce durch einen Platzhalter und vergleichen Sie ihn mit einer Datei in Ihrem Repository, nach jedem Deploy und regelmäßig. Verstoßmeldungen über report-to zeigen eine Änderung von der Browserseite, denn jede Meldung enthält den Richtlinientext und ob er erzwungen wurde.
Wegen der Nonce, die für jede Antwort neu sein muss. Ersetzen Sie sie vor dem Vergleich durch einen festen Platzhalter, etwa mit sed -E "s/'nonce-[^']+'/'nonce-X'/g", sonst meldet jeder Vergleich eine Änderung.
Der Browser erzwingt beide: eine Ressource wird nur geladen, wenn jede Richtlinie sie erlaubt. Ein zweiter Header lockert den ersten also nie, er kann aber unerwartet Ressourcen blockieren. Ein Content-Security-Policy-Report-Only-Header neben einem erzwingenden blockiert nichts und meldet nur.
Nein. Er zeigt, welche Befunde behoben oder neu sind und wie sich die Bewertungen geändert haben. Ein Wechsel auf Report-Only, eine entfernte Richtlinie oder eine neue Schwäche in einer bisher sauberen Richtlinie erscheint als Befund. Eine geänderte Host-Liste in einer ansonsten starken Richtlinie nicht; dafür vergleichen Sie den Header-Wert.
07 Im Detail
Eine Änderung zu erkennen ist ein Teil. Diese Ratgeber erklären, wie die Richtlinie und ihre Nachbarn überhaupt aussehen sollten.
Was jeder Response-Header bewirkt, welche Ihre Website senden sollte und wie Sie sie richtig konfigurieren.
Der stärkste Schutz gegen Cross-Site-Scripting: welche Direktiven Sie setzen, wie Sie eine Richtlinie im Report-Only-Modus einführen und wie Sie erzwingend werden, ohne Ihre Website zu zerlegen.
Strict-Transport-Security erklärt: was max-age, includeSubDomains und preload wirklich bewirken und warum die Preload-Liste eine Entscheidung ist, die Sie nicht schnell zurücknehmen können.
Secure, HttpOnly und SameSite: die Cookie-Attribute, die eine Sitzung vor Skripten und fremden Seiten schützen, samt Einstellungen für einen typischen Stack.
Wie eine unsichtbare Überlagerung aus einem Besucherklick eine Aktion auf Ihrer Website macht, und die zwei Header, die das verhindern: X-Frame-Options für ältere Clients, frame-ancestors für alles andere.
Welcher Teil Ihrer URLs an fremde Seiten geht, wenn ein Besucher weiterklickt: die fünf Werte, die zählen, was jeder davon preisgibt und welcher der sinnvolle Standard ist.
Kamera, Mikrofon, Standort und Bezahlfunktion für Ihre Website und alle eingebetteten Inhalte abschalten: die Syntax der Positivliste und warum eine nicht genannte Funktion keine abgeschaltete Funktion ist.
Die Einträge, die verhindern, dass jemand in Ihrem Namen Mails verschickt, und der, der Ihre DNS-Antworten ehrlich hält: was jeder leistet und in welcher Reihenfolge Sie sie einführen.
Die Datei, die Sicherheitsforschern sagt, wohin eine Schwachstelle gemeldet wird. Zwei Pflichtfelder, zehn Minuten Arbeit, und der Unterschied zwischen einer privaten und einer öffentlichen Meldung.
Ein Header, der heute stimmt, kann nach dem nächsten Deploy fehlen. Drei Wege, das zu bemerken: ein Cronjob mit curl, eine Prüfung in Ihrer CI-Pipeline und ein geplanter Scan, jeweils mit dem, was er findet und was nicht.
Weiterlesen
Jeder Ratgeber steht für sich, zusammen decken sie ab, worauf unser Scanner schaut. Wählen Sie den, der Ihrer nächsten Frage am nächsten kommt.
Ein kostenloser Scan liest Ihre ausgelieferte Content-Security-Policy, die Report-Only-Variante und eine Richtlinie im Meta-Tag und benennt jede Schwäche. Das ist der Stand, gegen den Sie vergleichen, und der erste Scan, von dem ein späterer Vergleich ausgeht.