Content-Security-Policy (CSP): HTTP-Security-Header zum Schutz vor XSS- und Code-Injection-Angriffen

Der HTTP-Security-Header Content-Security-Policy (CSP) dient zum Erkennen von sowie dem Schutz vor schädlichen Cross-Site-Scripting (XSS), Clickjacking und anderen Code-Injection-Angriffen. Hierfür wird dem Webbrowser mitgeteilt, welche Daten (JavaScript, Grafiken, CSS etc.) von welchen Quellen geladen und ausgeführt werden dürfen. Des Weiteren besteht die Möglichkeit unverschlüsselte und somit unsichere Verbindungen via HTTP auf verschlüsselte HTTPS-Verbindungen zu upgraden bzw. generell zu blocken.

Wie ihr die Content-Security-Policy auf eurer Website bzw. in eurer Webandwendung einsetzt, erfahrt ihr in diesem Beitrag.

Content-Security-Policy - Beschreibung & Funktionsweise

Mit der Content-Security-Policy kann, wie einleitend bereits erwähnt, gesteuert werden, welche Daten bzw. "Datentypen" der Webbrowser laden und ausführen darf. Zudem lässt sich festlegen, aus welchen Quellen diese Daten stammen müssen. Hierzu werden die erlaubten Daten per "Whitelisting" als vertrauenswürdig ausgewiesen.

Um den Webbrowser die zu verwendende Policy mitzuteilen, sendet der Webserver innerhalb der HTTP-Antwort den entsprechenden HTTP-Header mit:

HTTP/2 200 OK
date: Sun, 15 Mar 2020 14:30:45 GMT
content-type: text/html; charset=UTF-8
content-length: 2802
content-security-policy: default-src: 'self';
HTTP-Antwort mit Content-Security-Policy-Header

Das o.a. Beispiel enthält die einfache Standard-Policy default-src 'self'. Diese Policy weist den Webbrowser an, Daten nur von der selben Quelle/Herkunft (Origin) zu laden, wie die Webseite selbst. Außerdem wird das Ausführen von Inline-JavaScript unterbunden.

Beispiel - CSP in Aktion

An einem einfachen Beispiel soll die Funktionsweise verdeutlich werden. Angenommen ihr ruft eine Webseite mit folgendem Inhalt (nur ein Ausschnitt) auf:

<head>
    <script>
        console.log('JavaScript executed!');
    </script>
</head>
<body>
<p>Test-Seite</p>
</body>
HTML - Webseite mit Inline-JavaScript

Ohne Einsatz der Content-Security-Policy würde das Inline-JavaScript ganz normal ausgeführt werden und ihr würdet in eurer Webbrowser-Konsole die entsprechende Ausgabe sehen. Soll das Ausführen des Inline-JavaScripts unterbunden werden, so kann die o.a. default-src 'self'-Policy Verwendung finden. Anschließend führt der Webbrowser das eingebette JavaScript nicht mehr aus. Erkennbar ist das daran, dass ihr anstelle der Ausgabe JavaScript executed! eine Fehlermeldung angezeigt bekommt.

Beispiel für Content-Security-Policy

Generell gibt es diverse Policy-Direktiven mit der ihr eure Policy feingranular einstellen könnt. Beispielsweise lässt sich einstellen, dass Daten standardmäßig die gleiche Herkunft, wie die aufgerufene Webseite haben müssen. Grafiken hingegen können aus allen Quellen geladen werden. CSS-Dateien sollen wiederum nur von einer bestimmten Domain erlaubt sein. Als Policy könnte das dann wie folgt aussehen:

default-src 'self'; img-src *; style-src assets.my-domain.de;

Enforce- vs. Report-Modus

Die Content-Security-Policy kann in zwei Modi verwendet werden - ich nenne sie mal "Enforce"- und "Report"-Modus. Der "Report"-Modus bietet sich an, wenn man die Content-Security-Policy auf seiner Webseite neu einführen bzw. Änderungen an der bestehenden Policy vornehmen möchte. In diesem Modus werden die Policy-Anweisungen vom Webbrowser nicht angewendet, aber Verstöße werden an eine optional anzugebende URL gesendet bzw. alternativ in der Konsole des Webbrowsers ausgegeben. Mit dem "Report"-Modus kann eine Policy getestet werden ohne aufgrund einer Fehlkonfiguration die Webseite versehentlich "lahm" zu legen.

Die Aktivierung des "Report"-Modus unterscheidet sich zum "Enforce"-Modus nur im anzupassenden Header-Namen:

content-security-policy-report-only: default-src: 'self';
HTTP-Antwort mit Content-Security-Policy-Report-Only-Header

Im Webbrowser würde dann in der Konsole ein Hinweis erscheinen:

Beispiel für Content-Security-Policy im Report-Modus

Weiterführende Informationen zum "Report"-Modus bzw. dem Header: Content-Security-Policy-Report-Only

Hinweis "Enforce"- und "Report"-Modus gleichzeitig

Da sich die Header-Namen für die beiden Modi unterscheiden, ist es möglich beide gleichzeitig zu verwenden. Der Webbrowser berücksicht dann auch beide, wobei die Anweisungen der "Enforce"-Policy angewendet werden und die der "Report"-Policy bei Verstößen nur zur Benachrichtigungen führen.

Policy-Direktiven im Detail

Da es recht viele Policy-Direktiven gibt, beschränke ich mich im Folgenden auf die gebräuchlichsten Direktiven. Ein Liste aller Direktiven und möglichen Werte findet ihr hier: CSP-Direktiven.

default-src
Diese Angabe sollte prinzipiell immer verwendet werden, da sie als Fallback für alle anderen nicht explizit angegebenen "Fetch"-Direktiven gilt.

script-src
Quellen für Script-Dateien definieren und z.B. festlegen, ob Inline-JavaScript erlaubt

img-src
Quellen für Grafik-Dateien definieren und z.B. festlegen, ob Inline-Grafiken via Data-URL erlaubt

style-src
Quellen für CSS-Dateien definieren und z.B. festlegen, ob Inline-CSS erlaubt

font-src
Quellen für Font-Dateien definieren

form-action
Quellen für erlaubte "Submit"-URLs von Formularen definieren

frame-anchestors
Quellen definieren, die die eigene Webseite z.B. mittels Frames einbinden dürfen

block-all-mixed-content
verhindert das Laden von unverschlüsselten HTTP-Ressourcen, wenn Webseite mittels HTTPS geladen wurde

upgrade-insecure-requests
behandelt alle unverschlüsselten HTTP-URLs als HTTPS-URLs - die Ressourcen werden also immer per HTTPS geladen auch wenn sie im Quelltext per HTTP ausgewiesen sind

Anmerkung upgrade-insecure-requests

Die Verwendung von upgrade-insecure-requests ist nicht gleichzusetzen mit der Verwendung von HTTP-Strict-Transport-Security (HSTS). HSTS (wenn bereits im Webbrowser-Cache) greift schon beim Aufruf der eigentlichen Webseite und nicht nur für die in der Webseite eingebundenen Ressourcen.

Anzumerken sei auch, dass upgrade-insecure-requests vor block-all-mixed-content ausgewertet wird. Letztgenannter wird demnach nicht benötigt, weil nicht relevant, wenn erstgenannter gesetzt.

Inline-JavaScript erlauben

Nicht immer lässt sich Inline-JavaScript vermeiden. In solchen Fällen gibt es die folgenden 3 Möglichkeiten:

  • unsafe-inline
  • Hashes
  • Nonces

In der einfachsten und somit unsichersten Variante, erlaubt man einfach jegliches Inline-JavaScript:

script-src 'unsafe-inline'

Besser ist es aber nur bewusst ausgewählte JavaScript-Blöcke zu erlauben. Dazu können Hashes oder Nonces verwendet werden. Hashes eigenen sich für statische JavaScript-Blöcke, die sich nicht (dynamisch) ändern. In der Konsole von Chrome wird bspw. der Hash zu einem geblockten JavaScript-Block direkt angezeigt und man kann diesen ganz einfach kopieren und in die Policy integrieren:

script-src 'sha256-abcdefghi'

Für JavaScript-Blöcke die sich ändern können, sind Nonces die richtige Wahl. Hierfür wird einem Script-Block eine generierte Nonce zugewiesen:

<script nonce="djksdjiwdsjdis">
    console.log('JavaScript executed!');
</script>
HTML - Inline-JavaScript mit Nonce

Analog zu Hashes müssen die erlaubten Nonces in die Policy eingefügt werden:

script-src 'nonce-djksdjiwdsjdis'

Webbrowser-Unterstützung

Die Content-Security-Policy wird von allen gängigen Webbrowsern unterstützt (siehe caniuse.com).

Content-Security-Policy übertragen

Um den Webserver anzuweisen, den Content-Security-Policy-Header zum Client mitzusenden, muss dieser entsprechend konfiguriert werden.

Apache:
In der Konfigurationsdatei von Apache oder in der .htaccess ist folgende Anweisung zu hinterlegen:

Header set Content-Security-Policy "default-src 'self';"
Apache-Konfigurationsdatei bzw. .htacess

nginx:
Analog dazu ist für nginx in der Konfigurationsdatei Folgendes einzufügen:

add_header Content-Security-Policy "default-src 'self';";
nginx-Konfigurationsdatei

Alternative Möglichkeit zur Übertragung

Grundsätzlich besteht auch die Möglichkeit die Content-Security-Policy anstatt per HTTP-Header mittels meta-Tag auszuliefern:

<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
HTML - Referrer-Policy per meta-Element

Content-Security-Policy von coding.blatt

Folgende Policy-Direktiven verwende ich hier im Blog:

  • default-src 'none'
  • font-src 'self'
  • img-src 'self'
  • style-src 'self'
  • block-all-mixed-content
  • base-uri 'none'
  • frame-ancestors 'none'
  • form-action 'none'

Standardmäßig blockiere ich alles, um dann selektiv das Laden von Schriftarten, Grafiken und CSS ausschließlich von der Domain https://www.codingblatt.de zu erlauben. Das Ausführen von JavaScript wird hierdurch auch generell im Blog unterbunden.

Fazit

Die Content-Security-Policy ist ein weiterer Baustein zur Absicherung der eigenen Website bzw. Webanwendung vor schädlichen Angriffen. Ich würde sagen, dass es sich aktuell um den "mächtigsten" HTTP-Security-Header handelt, da durch die verschiedenen Policy-Direktiven diverse Angriffstypen- & szenarien abgewehrt bzw. erschwert werden können.

Feedback

Für Feedback zum Beitrag, seien es Fragen, Korrigierungen und/oder Anregungen, könnt ihr mir gerne eine Nachricht per E-Mail oder Mastodon schreiben (siehe Kontakt).