Neuigkeiten von trion.
Immer gut informiert.

CSP Nonce Header für Angular Anwendungen mit nginx Container

Angular

Durch CSP Header (Content Security Policy) lassen sich verschiedene Funktionen des Webbrowsers konfigurieren, um das Verhalten von Nachladen und auch Ausführen zusätzlicher oder dynamischer Inhalte zu beeinflussen. Damit lassen sich typische Einfallstore für Angriffe wie Cross-Site-Scripting (XSS) wirksam abstellen.
Ist die Webanwendung jedoch eine Single Page Anwendung (SPA) und auf dynamische Erzeugung von Inhalten angewiesen, führt eine globale Deaktivierung der Dynamik dazu, dass die Anwendung nicht mehr funktioniert. Es stellt sich daher die Frage, wie zwischen guten und potentiell schädlichen externen und dynamischen Inhalten unterschieden werden kann.

Dazu ist in allen Browsern inzwischen ein Verfahren implementiert, mit dem Entwickler solche Inhalte, die erwünscht sind, expliziert markieren können: sogenannte "Nonce" Markierungen. Aktuelle Frameworks, so auch Angular, bringen dazu auch speziellen Support mit.
Die Implementierung wird in diesem Beitrag anhand von nginx gezeigt.

CSP Nonce Policy Header

Ein Nonce ist ein schwer erratbarer, zufälliger Wert. Dieser sollte dabei auch lediglich für eine kurze Zeit gültig sein.
Mit der CSP (Content Security Policy) "nonce" wird so ein zufälliger Wert verwendet, um Inhalte auf einer Webseite zu autorisieren. Der nonce-Wert wird dabei vom Server generiert und sowohl in einem HTTP-Header als auch in den HTML-Tags der jeweiligen Skripte eingefügt. Dies verhindert die Ausführung von Skripten, die nicht ausdrücklich erlaubt sind, und schützt vor Angriffen wie Cross-Site Scripting (XSS). Die Prüfung erfolgt dabei automatisch durch den jeweiligen User-Agent (Browser), indem verglichen wird, ob der Header Wert und die Werte an den Tags identisch sind.

Das folgende Code-Beispiel zeigt den prinzipiellen Einsatz. Hier wird statt eines HTTP Headers ein http-equiv Meta-Tag für das Nonce verwendet.

Beispiel zur Verwendung von Nonces
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'nonce-123';">
    <title>Beispiel zur Verwendung von CSP Nonce</title>
</head>
<body>
    <h1>Hallo, CSP Nonce!</h1>
    <script nonce="123">
        console.log('OK, this will be loggd.');
    </script>
    <script>
        console.log('Denied, no nonce!');
    </script>
    <script nonce="guess">
        console.log('Denied, wrong nonce!');
    </script>
</body>
</html>

Generierung von CSP nonce mit nginx

Vor allem im Container-Umfeld hat der nginx Webserver eine große Beliebtheit erlangt. Er ist schlank, schnell und lässt sich dank vorhandenem Docker-Container-Image einfach betreiben.
Natürlich ist der hier gezeigte Ansatz auch auf andere Webserver übertragbar.

Sowohl im HTTP-Header, als auch im ausgelieferten Inhalt müssen die CSP Nonces übereinstimmen. Ein Ansatz dazu bei nginx ist die Verwendung des http_sub_module, eine Ersetzung (Substitution) von Inhalt zum Auslieferungszeitpunkt. Ob das aktuelle Container-Image mit dem dazu benötigten Modul ausgestattet ist, lässt sich durch die folgende Anweisung prüfen.

Validierung von http_sub_module im nginx build
$  docker run --rm -it nginx nginx -V 2>&1 | tr ' ' '\n' | grep -i 'http_sub_module'

Als Nonce wird ein zufälliger Wert benötigt. Dazu eignet sich zum Besipiel die ID des aktuellen Requests oder der TLS/SSL Session. In nginx kann dazu eine separate Variable deklariert werden.

Definition der cspNonce Variable in nginx auf Basis der HTTP Request-ID
set $cspNonce $request_id; # or $ssl_session_id;

Dieser Wert wird dann im CSP Header ausgegeben.

nginx HTTP CSP Header inklusive nonce Wert
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$cspNonce'; style-src 'self' 'nonce-$cspNonce'; style-src-attr 'unsafe-inline'; img-src 'self' blob:;";

Im HTML wird ein Platzhalter vorgesehen, der dann durch den jeweilligen Wert ausgetauscht wird. Das könnte dann wie im folgenden Beispiel aussehen.

Platzhalter für den jeweiligen Nonce Wert im HTML
<script nonce="CSP_NONCE">
// inline script
</script>

Damit der Wert nun noch an der richtigen Stelle bereitgestellt wird, muss das bereits erwähnte http_sub_module aktiviert werden. Für den im Beispiel verwendete Platzhalter CSP_NONCE könnte eine Konfiguration wie folgt aussehen.

Konfiguration im nginx um den Platzhalter zu ersetzen.
sub_filter_once off;
sub_filter_types *;
sub_filter CSP_NONCE $cspNonce;

Das zugehörige Beispiel HTML sieht dann wie folgt aus:

Beispiel HTML mit CSP_NONCE als Platzhalter
<!DOCTYPE html>
<html>
<head>
    <title>Beispiel zur Verwendung von CSP Nonce</title>
</head>
<body>
    <h1>Hallo, CSP Nonce!</h1>
    <script nonce="CSP_NONCE">
        console.log('OK, this will be loggd.');
    </script>
    <script>
        console.log('Denied, no nonce!');
    </script>
    <script nonce="guess">
        console.log('Denied, wrong nonce!');
    </script>
</body>
</html>

Nun kann man nach jedem Laden der Seite sehen, dass sich der Wert des Nonce ändert und in den HTTP Headern den passenden Wert vorfinden.

CSP Nonce in Angular

Um den CSP-Nonce für Angular verfügbar zu machen, muss in der index.html das Attribut ngCspNonce auf die Root-Komponente gesetzt werden. Das ist im nachfolgenden Listing gezeigt. Außerdem ist in dem Listing auch dargestellt, dass das nonce-Attribut auf statischen (inline)-Styles und -Scripten verwendet wird. Das ist für die korrekte Funktionsweise der Seite ebenfalls notwendig, da diese Scripte/Styles ansonsten die oben angegebene CSP verletzen würden.

Nun kann die Angular-App wie gewohnt gebaut werden. Dabei berücksichtigt Angular nun das ngCspNonce-Attribut, und generiert Code, um auch nachträglich/lazy geladene Bestandteile der Anwendung mit dem passenden Nonce zu versehen.

Beispiel Angular-index.html mit CSP_NONCE als Platzhalter
<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <title>My-CSP-Demo</title>
  <base href="/">
  <script nonce="CSP_NONCE">
    const version = '';
  </script>

  <style nonce="CSP_NONCE">
    html, body {
      margin: 0;
      background: #dd1d21;
    }
  </style>
</head>
<body>
  <app-root ngCspNonce="CSP_NONCE"></app-root>
</body>
</html>

Falls das ngCspNonce-Attribut aus irgendeinem Grund nicht gesetzt werden kann, so kann alternativ das Angular-Injection-Token CSP_NONCE beim Start der Anwendung provided werden.




Zu den Themen Kubernetes, Docker und Angular bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.