DOM-basierte Cross-Site-Scripting-Sicherheitslücken mit vertrauenswürdigen Typen verhindern

Krzysztof Kotowicz
Krzysztof Kotowicz

Unterstützte Browser

  • 83
  • 83
  • x
  • x

Quelle

DOM-basiertes Cross-Site-Scripting (DOM XSS) tritt auf, wenn Daten von einer vom Nutzer gesteuerten Quelle (z. B. ein Nutzername oder eine Weiterleitungs-URL aus dem URL-Fragment) eine Senke erreichen. Dies ist eine Funktion wie eval() oder ein Property-Setter wie .innerHTML, der beliebigen JavaScript-Code ausführen kann.

DOM XSS ist eine der häufigsten Sicherheitslücken im Web und kommt von Entwicklungsteams häufig versehentlich in ihre Anwendungen ein. Mit vertrauenswürdigen Typen erhalten Sie die Tools, mit denen Sie Anwendungen schreiben, Sicherheitsprüfungen durchführen und Anwendungen von DOM-XSS-Sicherheitslücken fernhalten können. Dazu werden gefährliche Web-API-Funktionen standardmäßig sicher eingerichtet. Vertrauenswürdige Typen sind als polyfill für Browser verfügbar, die sie noch nicht unterstützen.

Hintergrund

DOM XSS ist seit vielen Jahren eine der häufigsten und gefährlichsten Sicherheitslücken im Web.

Es gibt zwei Arten von Cross-Site-Scripting. Einige XSS-Sicherheitslücken werden durch serverseitigen Code verursacht, der den HTML-Code, aus dem die Website besteht, unsicher erstellt. Andere haben eine Grundursache auf dem Client, bei der der JavaScript-Code gefährliche Funktionen mit nutzergesteuerten Inhalten aufruft.

Wenn Sie serverseitiges XSS verhindern möchten, generieren Sie HTML keinen HTML-Code durch Verkettung von Strings. Verwenden Sie stattdessen sichere kontextbasierte Autoescaping-Vorlagenbibliotheken zusammen mit einer Nonce-basierten Content Security Policy zur zusätzlichen Fehlerbehebung.

Jetzt können Browser mithilfe von vertrauenswürdigen Typen auch dazu beitragen, das clientseitige DOM-basierte XSS zu verhindern.

API-Einführung

Vertrauenswürdige Typen funktionieren, indem die folgenden riskanten Senkenfunktionen gesperrt werden. Einige davon kennen Sie vielleicht schon, da Browseranbieter und Web-Frameworks aus Sicherheitsgründen Sie davon abhalten, diese Funktionen zu verwenden.

Bei vertrauenswürdigen Typen müssen Sie die Daten verarbeiten, bevor Sie sie an diese Senkenfunktionen übergeben. Die ausschließliche Verwendung eines Strings schlägt fehl, da der Browser nicht weiß, ob die Daten vertrauenswürdig sind:

Don'ts
anElement.innerHTML  = location.href;
Wenn vertrauenswürdige Typen aktiviert sind, gibt der Browser einen TypeError aus und verhindert die Verwendung einer DOM-XSS-Senke mit einem String.

Um anzugeben, dass die Daten sicher verarbeitet wurden, erstellen Sie ein spezielles Objekt – einen vertrauenswürdigen Typ.

Das sollten Sie tun:
anElement.innerHTML = aTrustedHTML;
  
Wenn vertrauenswürdige Typen aktiviert sind, akzeptiert der Browser ein TrustedHTML-Objekt für Senken, die HTML-Snippets erwarten. Es gibt auch die Objekte TrustedScript und TrustedScriptURL für andere sensible Senken.

Vertrauenswürdige Typen reduzieren die DOM XSS-Angriffsfläche Ihrer Anwendung erheblich. Es vereinfacht Sicherheitsprüfungen und ermöglicht es Ihnen, die typbasierten Sicherheitsprüfungen zu erzwingen, die beim Kompilieren, Linting oder Bündeln Ihres Codes zur Laufzeit im Browser durchgeführt werden.

Vertrauenswürdige Typen verwenden

Auf Meldungen zu Verstößen gegen die Content Security Policy vorbereiten

Sie können einen Report Collector wie den Open-Source-go-csp-collector oder ein kommerzielles Äquivalent bereitstellen. Sie können Verstöße auch im Browser beheben:

document.addEventListener('securitypolicyviolation',
    console.error.bind(console));

CSP-Header nur für Berichte hinzufügen

Fügen Sie Dokumenten, die zu vertrauenswürdigen Typen migriert werden sollen, den folgenden HTTP-Antwortheader hinzu:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Jetzt werden alle Verstöße an //my-csp-endpoint.example gemeldet, aber die Website funktioniert weiterhin. Im nächsten Abschnitt wird die Funktionsweise von //my-csp-endpoint.example erläutert.

Verstöße gegen vertrauenswürdige Typen identifizieren

Ab jetzt sendet der Browser jedes Mal, wenn vertrauenswürdige Typen einen Verstoß erkennen, einen Bericht an eine konfigurierte report-uri. Wenn Ihre Anwendung beispielsweise einen String an innerHTML übergibt, sendet der Browser den folgenden Bericht:

{
"csp-report": {
    "document-uri": "https://my.url.example",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 39,
    "column-number": 12,
    "source-file": "https://my.url.example/script.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
}
}

Dies besagt, dass in https://my.url.example/script.js in Zeile 39 innerHTML mit dem String aufgerufen wurde, der mit <img src=x beginnt. Mithilfe dieser Informationen können Sie eingrenzen, welche Codeteile möglicherweise DOM XSS einführen und geändert werden müssen.

Verstöße beheben

Es gibt mehrere Möglichkeiten, einen Verstoß gegen den vertrauenswürdigen Typ zu beheben. Sie können den beleidigenden Code entfernen, eine Bibliothek verwenden, eine Richtlinie für vertrauenswürdigen Typ erstellen oder zuletzt eine Standardrichtlinie erstellen.

Den betreffenden Code umschreiben

Es ist möglich, dass der nicht konforme Code nicht mehr benötigt wird oder ohne die Funktionen, die die Verstöße verursachen, neu geschrieben werden kann:

Das sollten Sie tun:
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
Don'ts
el.innerHTML = '';

Bibliothek verwenden

Einige Bibliotheken generieren bereits vertrauenswürdige Typen, die Sie an die Senkenfunktionen übergeben können. Sie können beispielsweise DOMPurify verwenden, um ein HTML-Snippet zu bereinigen und XSS-Nutzlasten zu entfernen.

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify unterstützt vertrauenswürdige Typen und gibt bereinigten HTML-Code zurück, der in ein TrustedHTML-Objekt eingebettet ist, damit der Browser keinen Verstoß auslöst.

Richtlinie für vertrauenswürdigen Typ erstellen

Manchmal können Sie den Code, der den Verstoß verursacht, nicht entfernen und es ist keine Bibliothek vorhanden, um den Wert zu bereinigen und einen vertrauenswürdigen Typ für Sie zu erstellen. In diesen Fällen können Sie selbst ein vertrauenswürdiges Objekt erstellen.

Erstellen Sie zuerst eine Richtlinie. Richtlinien sind Factorys für vertrauenswürdige Typen, die bestimmte Sicherheitsregeln für ihre Eingabe erzwingen:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

Mit diesem Code wird eine Richtlinie namens myEscapePolicy erstellt, die mithilfe ihrer Funktion createHTML() TrustedHTML-Objekte erstellen kann. Die definierten Regeln enthalten HTML-Escape-Zeichen für <, um die Erstellung neuer HTML-Elemente zu verhindern.

So verwenden Sie die Richtlinie:

const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x onerror=alert(1)>'

Standardrichtlinie verwenden

Manchmal kann der problematische Code nicht geändert werden, z. B. wenn Sie eine Drittanbieterbibliothek aus einem CDN laden. Verwenden Sie in diesem Fall eine Standardrichtlinie:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

Die Richtlinie mit dem Namen default wird immer dann verwendet, wenn ein String in einer Senke verwendet wird, die nur vertrauenswürdigen Typ akzeptiert.

Zur Erzwingung der Content Security Policy wechseln

Wenn Ihre Anwendung keine Verstöße mehr verursacht, können Sie damit beginnen, vertrauenswürdige Typen zu erzwingen:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Unabhängig davon, wie komplex Ihre Webanwendung ist, kann eine DOM-XSS-Sicherheitslücke nur durch den Code in einer Ihrer Richtlinien entstehen. Sie können dies noch weiter sperren, indem Sie die Erstellung von Richtlinien einschränken.

Weitere Informationen