Cross-Site-Scripting (XSS) mit einer strengen Content Security Policy (CSP) abschwächen

Lukas Weichselbaum
Lukas Weichselbaum

Unterstützte Browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 52.
  • Safari: 15.4

Quelle

Cross-Site-Scripting (XSS) schädliche Skripts in eine Webanwendung einschleusen. die größten Schwachstellen im Web.

Content Security Policy (CSP) bietet eine zusätzliche Sicherheitsebene zur Vermeidung von XSS. So konfigurieren Sie eine CSP: fügen Sie den Content-Security-Policy-HTTP-Header einer Webseite hinzu und legen Sie Werte fest, welche Ressourcen der User-Agent für diese Seite laden kann.

Auf dieser Seite wird erläutert, wie Sie mit einer auf Nonces oder Hashes basierenden CSP anstelle der gängigen CSPs, die auf Host-Zulassungslisten basieren, da sie in den meisten Konfigurationen umgangen werden können.

Schlüsselbegriff: Eine Nonce ist eine Zufallszahl, die nur einmal verwendet wird und mit der Sie eine <script>-Tag als vertrauenswürdig eingestuft.

Schlüsselbegriff: Eine Hash-Funktion ist eine mathematische Funktion, die eine Eingabe umwandelt. Wert in einen komprimierten numerischen Wert umgewandelt, der als Hash bezeichnet wird. Sie können ein Hash (z. B. SHA-256), um eine Inline- <script>-Tag als vertrauenswürdig eingestuft.

Eine Content Security Policy, die auf Nonces oder Hashes basiert, strikte CSP. Wenn eine Anwendung eine strikte CSP verwendet, können Angreifer, die HTML finden, können Injection-Fehler den Browser in der Regel nicht zwingen, bösartige Skripts in einem angreifbaren Dokument. Das liegt daran, dass eine strikte CSP ermöglicht gehashte Skripts oder Skripts mit dem korrekten Nonce-Wert, der auf der ein, damit Angreifer das Skript nur ausführen können, wenn sie die korrekte Nonce kennen für eine bestimmte Antwort.

Warum sollten Sie eine strikte CSP verwenden?

Wenn Ihre Website bereits eine CSP mit dem Format script-src www.googleapis.com hat, ist das wohl nicht effektiv bei websiteübergreifenden. Diese Art von CSP wird als CSPs auf der Zulassungsliste Sie erfordern viel Anpassung und können von Angreifern umgangen werden.

Mit strikten CSPs, die auf kryptografischen Nonces oder Hashes basieren, vermeiden Sie diese Schwierigkeiten.

Strenge CSP-Struktur

Eine grundlegende strenge Content Security Policy verwendet eine der folgenden HTTP-Antworten Header:

Nonce-basierte strikte CSP

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
Funktionsweise einer nonce-basierten strengen CSP

Hash-basierte strikte CSP

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Die folgenden Eigenschaften machen eine CSP wie diese „strikt“ und damit sicher:

  • Sie verwendet die Nonces 'nonce-{RANDOM}' oder die Hashes 'sha256-{HASHED_INLINE_SCRIPT}' um anzugeben, mit welchen <script>-Tags der Entwickler der Website vertraut. im Browser des Nutzers.
  • Er legt 'strict-dynamic' fest. den Aufwand für die Bereitstellung einer Nonce- oder Hash-basierten CSP durch das automatische das Ausführen von Skripts ermöglicht, die von einem vertrauenswürdigen Skript erstellt werden. Dies gilt auch für ermöglicht die Nutzung der meisten JavaScript-Bibliotheken und -Widgets von Drittanbietern.
  • Er basiert nicht auf URL-Zulassungslisten und beeinträchtigt daher nicht Gängige Umgehungen der CSP.
  • Nicht vertrauenswürdige Inline-Skripts wie Inline-Event-Handler oder javascript: werden blockiert. URIs.
  • object-src wird eingeschränkt, gefährliche Plug-ins wie Flash zu deaktivieren.
  • base-uri wird eingeschränkt, das Einfügen von <base>-Tags zu blockieren. Dadurch wird verhindert, und Angreifer daran hindern, den Speicherort von Skripts zu ändern, die von relativen URLs geladen werden.

Eine strikte CSP verwenden

Für eine strikte CSP müssen Sie Folgendes tun:

  1. Entscheiden Sie, ob Ihre Anwendung eine Nonce- oder Hash-basierte CSP festlegen soll.
  2. Kopieren Sie die CSP aus dem Abschnitt Strikte CSP-Struktur und legen Sie sie fest. in Ihrer Anwendung als Antwortheader.
  3. Refaktorieren von HTML-Vorlagen und clientseitigem Code, um Muster zu entfernen, die nicht mit der CSP kompatibel.
  4. Stellen Sie Ihre CSP bereit.

Sie können Lighthouse (Version 7.3.0 und höher mit dem Flag --preset=experimental) Best Practices-Prüfung um zu prüfen, ob Ihre Website eine CSP hat und streng genug, um gegen XSS wirksam zu sein.

<ph type="x-smartling-placeholder">
</ph> Leuchtturm
  Warnung melden, dass im Erzwingungsmodus keine CSP gefunden wurde.
Wenn Ihre Website keine CSP hat, wird diese Warnung in Lighthouse angezeigt.

Schritt 1: Entscheiden, ob Sie eine Nonce- oder Hash-basierte CSP benötigen

So funktionieren die beiden Arten strikter CSPs:

Nonce-basierte CSP

Bei einer nonce-basierten CSP generieren Sie eine Zufallszahl zur Laufzeit und fügen sie in und mit jedem Skript-Tag auf Ihrer Seite verknüpfen. Ein Angreifer dürfen kein schädliches Skript in eure Seite einfügen oder ausführen, die richtige zufällige Zahl für dieses Skript. Dies funktioniert nur, wenn die Zahl nicht erraten lassen und für jede Antwort zur Laufzeit neu generiert.

Verwende eine nonce-basierte CSP für HTML-Seiten, die auf dem Server gerendert werden. Für diese Seiten können Sie für jede Antwort eine neue Zufallszahl erstellen.

Hash-basierte CSP

Bei einer hashbasierten CSP wird der Hash aller Inline-Skript-Tags dem CSP hinzugefügt. Jedes Skript hat einen anderen Hash. Ein Angreifer kann keine bösartigen da der Hash-Wert des Skripts im CSP, damit sie ausgeführt werden kann.

Verwenden Sie eine hashbasierte CSP für HTML-Seiten, die statisch bereitgestellt werden oder für Seiten, die im Cache gespeichert. Sie können z. B. eine hashbasierte CSP für Single-Page-Web- Anwendungen, die auf Frameworks wie Angular, React oder anderen basieren, statisch ohne serverseitiges Rendering bereitgestellt werden.

Schritt 2: Strenge CSP festlegen und Skripts vorbereiten

Beim Einrichten einer CSP haben Sie mehrere Möglichkeiten:

  • Nur-Bericht-Modus (Content-Security-Policy-Report-Only) oder Erzwingungsmodus (Content-Security-Policy). Im Modus „Nur Berichterstellung“ blockiert die CSP damit nichts beschädigt wird. Sie sehen aber Fehler und erhalten für alles, was blockiert worden wäre. Vor Ort, wenn Sie CSP festlegen, spielt das keine Rolle, denn beide Modi zeigen an, in der Browserkonsole angezeigt. Wenn ja, können Sie mit dem Erzwingungsmodus Ressourcen, die vom CSP-Entwurf blockiert werden, da das Blockieren einer Ressource nicht funktioniert. Der Nur-Bericht-Modus wird später am nützlichsten (siehe Schritt 5).
  • Header oder HTML-<meta>-Tag. Für die lokale Entwicklung kann das Tag <meta> ist sehr praktisch, um die CSP zu optimieren und schnell zu sehen, wie sich das auf Ihre Website auswirkt. Allerdings gilt: <ph type="x-smartling-placeholder">
      </ph>
    • Wenn Sie die CSP später in der Produktion bereitstellen, sollten Sie sie so festlegen: einem HTTP-Header.
    • Wenn Sie für die CSP den Modus „Nur Berichterstellung“ verwenden möchten, müssen Sie sie als , da CSP-Meta-Tags den Berichtsmodus nicht unterstützen.

Option A: Nonce-basierte CSP

Lege die folgende Content-Security-Policy-HTTP-Antwort fest: in Ihrer Anwendung ein:

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Nonce für CSP generieren

Eine Nonce ist eine Zufallszahl, die nur einmal pro Seitenaufbau verwendet wird. Nonce-basierte Die CSP kann XSS nur abschwächen, wenn Angreifer den Nonce-Wert nicht erraten können. A Die CSP-Nonce muss Folgendes sein:

  • Einen kryptografisch starken Zufallswert (idealerweise 128 oder mehr Bit)
  • Für jede Antwort neu generiert
  • Base64-codiert

Hier sind einige Beispiele dafür, wie Sie eine CSP-Nonce in serverseitigen Frameworks hinzufügen:

const app = express();

app.get('/', function(request, response) {
  // Generate a new random nonce value for every response.
  const nonce = crypto.randomBytes(16).toString("base64");

  // Set the strict nonce-based CSP response header
  const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`;
  response.set("Content-Security-Policy", csp);

  // Every <script> tag in your application should set the `nonce` attribute to this value.
  response.render(template, { nonce: nonce });
});

<script>-Elementen ein nonce-Attribut hinzufügen

Bei einer nonce-basierten CSP muss jedes <script>-Element haben ein nonce-Attribut, das mit der zufälligen Nonce übereinstimmt Wert, der im CSP-Header angegeben ist. Alle Skripts können dieselben Nonce. Der erste Schritt besteht darin, diese Attribute allen Skripts hinzuzufügen, damit das Die CSP lässt sie zu.

Option B: Hash-basierter CSP-Antwortheader

Lege die folgende Content-Security-Policy-HTTP-Antwort fest: in Ihrer Anwendung ein:

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Für mehrere Inline-Skripts lautet die Syntax wie folgt: 'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'

<ph type="x-smartling-placeholder">

Quellskripte dynamisch laden

CSP-Hashes werden browserübergreifend nur für Inline-Skripts unterstützt. Sie müssen alle Drittanbieter-Skripts dynamisch mit einem Inline-Skript laden. Hashes für Quellskripts werden in Browsern nicht gut unterstützt.

<ph type="x-smartling-placeholder">
</ph>
Beispiel für das Inline-Format von Scripts
Von der CSP zugelassen
<script>
  var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];

  scripts.forEach(function(scriptUrl) {
    var s = document.createElement('script');
    s.src = scriptUrl;
    s.async = false; // to preserve execution order
    document.head.appendChild(s);
  });
</script>
Damit dieses Script ausgeführt werden kann, müssen Sie den Hash des Inline-Skripts berechnen und fügen Sie ihn dem CSP-Antwortheader hinzu, wobei {HASHED_INLINE_SCRIPT} ersetzt wird. . Um die Anzahl der Hashes zu reduzieren, können Sie alle Hashes inline zusammenführen. in einem einzigen Skript zusammenfassen. Weitere Informationen dazu finden Sie hier: Beispiel und der zugehörige Code enthält.
Von der CSP blockiert
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
Die CSP blockiert diese Skripts, da nur Inline-Skripts gehasht werden können.
<ph type="x-smartling-placeholder">

Überlegungen beim Laden von Skripts

Im Beispiel für ein Inline-Script wird s.async = false hinzugefügt, foo wird vor bar ausgeführt, auch wenn bar wird zuerst geladen. In diesem Snippet: s.async = false blockiert den Parser nicht, während die Skripts geladen werden, da die Skripts dynamisch hinzugefügt werden. Der Parser stoppt nur, während die Skripts ausgeführt werden, wie: würde dies für async Scripts gelten. Mit diesem Snippet Hinweis:

  • Es kann sein, dass eines oder beide Skripts ausgeführt werden, bevor das Dokument abgeschlossen ist. wird heruntergeladen. Wenn Sie möchten, dass das Dokument fertig ist, Skripts ausgeführt werden, warten Sie auf das DOMContentLoaded-Ereignis, bevor hängen Sie die Skripts an. Wenn dies ein Leistungsproblem verursacht, Wenn der Download von Skripts nicht früh genug beginnt, verwenden Sie Tags zum Vorabladen früher auf der Seite.
  • defer = true macht nichts. Falls nötig das Skript manuell bei Bedarf ausführen.

Schritt 3: HTML-Vorlagen und clientseitigen Code refaktorieren

Inline-Event-Handler (z. B. onclick="…", onerror="…") und JavaScript-URIs (<a href="javascript:…">) können zum Ausführen von Skripts verwendet werden. Das bedeutet, dass ein kann ein Angreifer, der einen XSS-Fehler findet, diese Art von HTML einschleusen und JavaScript Bei einer Nonce- oder Hash-basierten CSP ist die Verwendung dieser Art von Markup verboten. Wenn auf deiner Website eines dieser Muster verwendet wird, musst du es in sicherere Alternativen.

Wenn Sie die CSP im vorherigen Schritt aktiviert haben, sehen Sie CSP-Verstöße in wenn die CSP ein inkompatibles Muster blockiert.

<ph type="x-smartling-placeholder">
</ph> Berichte zu CSP-Verstößen in der Chrome-Entwicklerkonsole
Konsolenfehler für blockierten Code.

In den meisten Fällen lässt sich das Problem ganz einfach beheben:

Inline-Event-Handler refaktorieren

Von der CSP zugelassen
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
Die CSP lässt Event-Handler zu, die über JavaScript registriert werden.
Von der CSP blockiert
<span onclick="doThings();">A thing.</span>
Die CSP blockiert Inline-Event-Handler.

javascript: URIs refaktorieren

Von der CSP zugelassen
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
Die CSP lässt Event-Handler zu, die über JavaScript registriert werden.
Von der CSP blockiert
<a href="javascript:linkClicked()">foo</a>
Die CSP blockiert „javascript: URIs“.

eval() aus JavaScript entfernen

Wenn Ihre Anwendung eval() zum Konvertieren von JSON-String-Serialisierungen in JS verwendet -Objekten enthält, sollten Sie diese Instanzen in JSON.parse() refaktorieren. schneller.

Wenn Sie nicht alle Verwendungen von eval() entfernen können, können Sie trotzdem eine strikte nonce-basierte Verwendung festlegen CSP, aber Sie müssen das CSP-Keyword 'unsafe-eval' verwenden, ist etwas weniger sicher.

Diese und weitere Beispiele für solche Refaktorierungen finden Sie in dieser strengen CSP. codelab:

Schritt 4 (optional): Fallbacks hinzufügen, um alte Browserversionen zu unterstützen

Unterstützte Browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 52.
  • Safari: 15.4

Quelle

Wenn ältere Browserversionen unterstützt werden müssen, gehen Sie so vor:

  • Wenn du strict-dynamic verwendest, musst du https: als Fallback für frühere Versionen hinzufügen Versionen von Safari. Dabei gilt Folgendes: <ph type="x-smartling-placeholder">
      </ph>
    • Alle Browser, die strict-dynamic unterstützen, ignorieren das https:-Fallback. Dadurch wird die Richtlinie nicht weniger streng.
    • In alten Browsern können extern generierte Skripts nur geladen werden, wenn sie von einem HTTPS-Ursprung. Das ist weniger sicher als eine strikte CSP, verhindert einige häufige XSS-Ursachen wie das Einschleusen von javascript:-URIs.
  • Um die Kompatibilität mit sehr alten Browserversionen (mindestens 4 Jahre) sicherzustellen, können Sie unsafe-inline als Fallback. unsafe-inline wird von allen aktuellen Browsern ignoriert wenn eine CSP-Nonce oder -Hash vorhanden ist.
Content-Security-Policy:
  script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';

Schritt 5: CSP bereitstellen

Nachdem Sie bestätigt haben, dass Ihre CSP keine legitimen Skripts in Ihrem Entwicklungsumgebung haben, können Sie Ihre CSP zuerst in der Staging-Umgebung und dann in Ihrem Produktionsumgebung:

  1. (Optional) Stellen Sie die CSP mithilfe des Content-Security-Policy-Report-Only-Header. Der Nur-Bericht-Modus ist nützlich, eine potenziell funktionsgefährdende Änderung wie eine neue CSP in der Produktion zu testen, CSP-Einschränkungen durchzusetzen. Im Modus „Nur Berichterstellung“ werden sich auf das Verhalten Ihrer App auswirken, der Browser generiert jedoch weiterhin Konsolenfehler. und Verstöße melden, wenn Muster erkannt werden, die mit Ihrer CSP nicht kompatibel sind, damit Sie sehen, was für Ihre Endnutzer nicht gut funktioniert hätte. Weitere Informationen Weitere Informationen zur Reporting API
  2. Wenn Sie sicher sind, dass Ihre CSP keine Unterbrechung für Ihre Website Stellen Sie die CSP mit dem Antwortheader Content-Security-Policy bereit. Mi. empfehlen wir, die CSP serverseitig mit einem HTTP-Header einzurichten, als ein <meta>-Tag. Nachdem Sie diesen Schritt abgeschlossen haben, beginnt Ihre CSP um Ihre App vor XSS zu schützen.

Beschränkungen

Eine strikte CSP bietet in der Regel eine starke zusätzliche Sicherheitsebene, XSS zu mindern. In den meisten Fällen reduziert die CSP die Angriffsfläche deutlich, indem gefährliche Muster wie javascript:-URIs abgelehnt. Je nach Typ des der verwendeten CSP (Nonces, Hashes, mit oder ohne 'strict-dynamic'), gibt es gibt es Fälle, in denen die CSP Ihre App nicht auch schützt:

  • Wenn Sie ein Script Nonce nutzen, aber direkt in den Textkörper oder die src-Parameters dieses <script>-Elements.
  • Bei Einschleusungen in die Speicherorte dynamisch erstellter Skripts (document.createElement('script')), auch in Bibliotheksfunktionen die script-DOM-Knoten basierend auf den Werten ihrer Argumente erstellen. Dieses enthält einige gängige APIs wie .html() von jQuery sowie .get() und .post() in jQuery < 3.0
  • Vorlageneinschleusungen in alten AngularJS-Anwendungen Ein Angreifer die in eine AngularJS-Vorlage eingefügt werden kann, Beliebiges JavaScript ausführen.
  • Wenn die Richtlinie 'unsafe-eval' enthält, Einschleusungen in eval(), setTimeout() und einige andere selten verwendete APIs.

Entwickler und Security Engineers sollten diese Mustern bei Codeüberprüfungen und Sicherheitsaudits. Weitere Informationen finden Sie diese Fälle in Content Security Policy: A Successful Mess Between Härtung und Risikominderung.

Weitere Informationen