Content-Security-Policy Header richtig konfigurieren
Was macht der Content-Security-Policy Header?
Mit dem Content-Security-Policy Header kann festgelegt werden, aus welchen Quellen eine Webseite Inhalte laden darf. Durch die Einschränkung auf bestimmte, vertrauenswürdige, Quellen, können Angriffe wie Cross Site Scripting (XSS) verhindert werden.
Was können die Auswirkungen eines nicht gesetzten Content-Security-Policy Headers sein?
Die wenigsten Webseiten kommen nur mit den Inhalten aus, die direkt auf ihrer eigenen Seite vorhanden sind. In der Regel werden auch Inhalte aus anderen Quellen auf einer Webseite eingebunden. Beispiele dafür sind etwa:
- Bilder (oder generell größere Dateien) die durch ein CDN geladen werden
- Werbung die von externen Servern geladen wird
- Styling Informationen (CSS) die von externen Servern geladen werden
- Skript Dateien und JS Bibliotheken die von externen Servern geladen werden
Das ist erstmal kein Problem, im Gegenteil: Die Größe der meisten Webseiten würde deutlich steigen, wenn all diese Elemente selbst eingebunden werden müssten.
Problematisch wird es aber dann, wenn auf der Webseite eine XSS Schwachstelle vorhanden ist. Ein Angreifer kann in diesem Fall seinen eigenen JavaScript Code in die Webseite integrieren und beispielsweise ein bösartiges Skript von seinem Server nachladen:
Dieses bösartige Skript ist nun in die Webseite integriert und wird ausgeführt, wenn diese von einem User aufgerufen wird:
Ein Beispiel dafür wäre ein Cryptomining Skript. Immer dann, wenn ein User die Webseite aufruft, werden die Ressourcen seines Browsers dazu genutzt, um Kryptowährungen zu schürfen. Das mag bei einem einzelnen User keinen großen Effekt haben, bei stark frequentierten Webseiten, die täglich von zigtausenden Menschen aufgerufen werden, können dabei aber leicht erhebliche Summen zusammenkommen.
Mit dem Content-Security-Policy Header können dagegen sichere Quellen definiert werden, aus denen die Webseite Inhalte laden darf:
In diesem Fall sind also vier Quellen angegeben, aus denen Inhalte geladen werden können. Selbst wenn nun eine XSS Schwachstelle auf der Webseite vorhanden sein sollte, wird das Skript des Angreifers nicht geladen, da sein Server im Content-Security-Policy Header nicht als sichere Quelle definiert wurde.
Welche Werte gibt es für den Content-Security-Policy Header?
Für die folgenden Arten von Content können Einschränkungen vorgenommen werden:
- default-src (Default, wenn keine explizite Einstellung getroffen wurde)
- script-src (Gibt an, aus welchen Quellen Skripte geladen werden dürfen)
- style-src (Gibt an, aus welchen Quellen Styling Informationen (CSS) geladen werden dürfen)
- img-src (Gibt an, aus welchen Quellen Bilder geladen werden dürfen)
- connect-src (Gibt an, auf welche URLs Skripte zugreifen können)
- font-src (Gibt an, aus welchen Quellen Schriftarten geladen werden können)
- object-src (Gibt an, aus welchen Quellen Plugins geladen werden können)
- media-src (Gibt an, aus welchen Quellen Audio oder Video Dateien geladen werden können)
- frame-src (Gibt an, aus welchen Quellen iFrames geladen werden können)
- sandbox (Erfordert eine Sandbox für die folgende Ressource, z.B. sandbox allow-scripts)
- report-uri (Angabe einer URL an die Verstöße gegen die Policy gemeldet werden (per POST Request))
- child-src (Gibt valide Quellen für Web Worker an)
- form-action (Gibt valide Quellen für HTML Formulare an)
- frame-ancestors (Gibt valide Quellen für das Einbetten in iFrames an)
- plugin-types (Gibt valide MIME Types für Plugins an)
- base-uri (Gibt URLs an, die im src Attribut des HTML Base Tags verwendet werden können)
- report-to (Definiert eine Reporting Group für den http Report-To Response Header)
- worker-src (Definiert welche URLs als Worker, Shared Worker oder Service Worker geladen werden können)
- manifest-src (Definiert Quellen aus denen ein Application Manifest geladen werden kann)
- prefetch-src (Definiert Quellen für Request Prefetch und Prerendering)
- navigate-to (Schränkt die URLs ein, zu denen ein Dokument navigieren kann)
Die folgenden Werte können dann für die jeweilige Content Art festgelegt werden:
- * (Wildcard, alles ist erlaubt)
- ‘none’ (verbietet das Laden von Content aus jeglicher Quelle)
- ‘self’ (erlaubt das Laden von Ressourcen die denselben Ursprung haben)
- data: (erlaubt das Laden über das Data Schema, z.B. Base64 codierte Bilder)
- example.com (erlaubt das Laden von der angegebenen Domain)
- *.example.com (Erlaubt das Laden von allen Subdomains der angegebenen Domain)
- https://example.com (Erlaubt das Laden nur über eine https Verbindung und nur über die angegebene Domain)
- https: (Erlaubt das Laden von jeder Domain, aber nur über https Verbindungen)
- ‘unsafe-inline’ (Erlaubt die Nutzung von Inline Ressourcen, z.B. <script></script>)
- ‘unsafe-eval’ (Erlaubt unsichere dynamische Code Ausführung (z.B. durch JavaScript eval())
- ‘sha256-‘ (Erlaubt die Ausführung eines Skripts oder CSS Files wenn sein Hash dem Hash im Header entspricht)
- Unterstützt werden: SHA256, SHA384 und SHA512
- ‘nonce-‘ (Erlaubt die Ausführung eines Skripts wenn im Skript Tag der angegebene Nonce steht)
- Der Nonce sollte zufällig sein und nicht wiederverwendet werden
- ‘strict-dynamic’ (Erlaubt einem erlaubten Skript zusätzliche Skripte nachzuladen)
Einschränkungen können nun wie folgt vorgenommen werden: Zunächst wird die Art des Contents angegeben (z.B. img-src), anschließend der Wert für diesen Content angegeben (z.B. ‘self‘). Mit dieser Einschränkung können dann nur noch Bilder geladen werden, die sich auf der eigenen Webseite befinden (z.B. aus dem /uploads Directory). Bilder aus anderen Quellen werde nicht mehr dargestellt.
Beispiele
- default-src ‘self‘
- Falls für eine Quelle keine Einschränkung getroffen wurde, ist das Laden per Default nur von der eigenen Seite erlaubt. Über die explizit Definition einer Quelle (z.B. img-src *) kann die Default Einstellung überschrieben werden. In diesem Fall wäre das Laden von Bildern aus beliebiger Quelle möglich. Nur wenn keine img-src angegeben ist, greift der Default Fall und beschränkt das Laden auf die eigene Webseite
- script-src ‘none’
- Verbietet das Laden jeglicher Skripte, auch solche die sich auf der eigenen Webseite befinden
- style-src ‘self’
- Erlaubt das Laden von Styling Informationen von derselben Webseite (die Webseite example.com kann also nur CSS Dateien laden, die auf example.com zu finden sind. Dateien auf externen Webseiten (z.B. extern.com/css) werden nicht geladen)
- font-src domain.fontspace.com
- Erlaubt das Laden von Schriftarten nur von der Domain fontspace.com
- media-src *.example.com
- Erlaubt das Laden von Media Dateien von allen Subdomains der Domain example.com (z.B. videos.example.com, audio.example.com, test.example.com)
- img-src https://pixabay.com
- Erlaubt das Laden von Bildern nur über eine https Verbindung von pixabay.com
- script-src ‘sha256-123456789‘
- Erlaubt die Ausführung des Skriptes, das den SHA-256 Hash 123456789 hat
- script-src ‘nonce-test123’
- Erlaubt die Ausführung eines Skrips bei dem im Skript Tag der Nonce test123 steht (<script nonce=”test123″>)
Mehrere Einschränkungen werden durch Semikolons getrennt:
- style-src ‘self’; font-src ‘self’, img-src ‘none’; script-src https://example.com
- CSS Dateien und Schriftarten können nur von der eigenen Webseite geladen werden, Bilder können überhaupt nicht geladen werden und Skripte können nur über eine https Verbindung von example.com geladen werden.
Man sieht anhand der Menge an möglichen Einstellungen schnell, dass die Konfiguration des Content-Security-Policy Headers alles andere als trivial ist. Legt man die Einschränkungen zu restriktiv fest, wird man schnell merken, dass auf einmal wesentliche Teile der Webseite nicht mehr korrekt funktionieren.
Für die richtige Konfiguration des Headers ist dementsprechend einiges an Testaufwand erforderlich. Wenn möglich sollte man aber zumindest versuchen, das Laden von Skripten auf die eigene Seite (self) zu beschränken, um zu verhindern, dass ein bösartiges Skript vom Server eines Angreifers geladen werden kann.
Auf welche Werte sollte der Content-Security-Policy Header gesetzt sein?
Natürlich ist es wichtig, dass der Header keine legitimen Ressourcen blockiert. Wäre dies der Fall, dann wäre möglicherweise die Webseite nicht mehr funktionsfähig (wenn beispielsweise ein wichtiges JS Skript nicht geladen werden kann).
Vor der Konfiguration des Headers ist es also wichtig zu wissen, von welchen Quellen die Webseite regulär Inhalte lädt. Dazu kann die Netzwerkanalyse der Entwickler Werkzeuge verwendet werden (F12 drücken und dann den Reiter Netzwerkanalyse aufrufen).
Für die Webseite Pixabay.com sieht man dort beispielsweise, dass diese sowohl Inhalte von einem externen CDN lädt, von internen Quellen, von einem Subdomain CDN und von externen Domains:
Diese Quellen müssten also auch weiterhin erlaubt werden, wenn der CSP Header gesetzt wird. Zunächst müssen also alle externen Quellen identifiziert werden, von denen die Webseite regulär Inhalte lädt.
Bestimmte Elemente werden dabei möglicherweise auch nur auf einzelnen Unterseiten genutzt. Ein bestimmtes JavaScript Skript wird vielleicht nur für die Umsetzung der Bezahlfunktion bei einem Onlineshop genutzt und dementsprechend nur auf der „Kasse“ Seite eingebunden aber nicht auf den Produktdetailseiten oder der Landingpage.
Um die legitimen Quellen zu identifizieren, sollte man also alle Unterseiten der Webseite aufrufen und prüfen, von wo sie Inhalte laden.
Achtung: Während manche Header (z.B. der X-Content-Type-Options Header) sehr einfach zu setzen sind, kann der Content-Security-Policy Header durchaus tricky sein. Wenn man den Header setzt, sollte man also in jedem Fall ausführlich testen, ob auch alle Bereiche der Webseite so aussehen und so funktionieren wie zuvor.
Da das korrekte Setzen des CSP Headers nicht trivial ist, gibt es von Google zudem auch eine Lösung, um das Ergebnis auf seine Sicherheit zu überprüfen:
Auf dieser Seite kann man die Content-Security-Policy, für die man sich entschieden hat, eintragen und durch die Webseite evaluieren lassen.