DOM-basiertes Cross-Site-Scripting (DOM XSS) findet statt, wenn Daten aus einer nutzergesteuerten Quelle (z. B. ein Nutzername oder eine vom URL-Fragment entnommene Weiterleitungs-URL) eine Senke erreichen. Dabei handelt es sich um eine Funktion wie eval()
oder einen Attribut-Setter wie .innerHTML
, der beliebigen JavaScript-Code ausführen kann.
DOM XSS ist eine der häufigsten Sicherheitslücken im Web. Es kommt häufig vor, dass Entwicklungsteams diese Sicherheitslücke versehentlich in ihren Anwendungen einführen. Mit vertrauenswürdigen Typen können Sie gefährliche Web-API-Funktionen standardmäßig sicher machen, damit Sie Anwendungen schreiben, Sicherheitsprüfungen ausführen und Anwendungen frei von DOM XSS-Sicherheitslücken halten können. 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 für die Website unsicher erstellt. Andere haben eine Ursache auf dem Client, weil der JavaScript-Code gefährliche Funktionen mit vom Nutzer kontrollierten Inhalten aufruft.
Um serverseitiges XSS zu verhindern, generieren Sie keinen HTML-Code durch das Verketten von Strings. Verwenden Sie stattdessen sichere kontextabhängige Vorlagenbibliotheken mit automatischem Escape-Code und eine nicht verwendete Content Security Policy, um Fehler zusätzlich zu beheben.
Browser können jetzt mithilfe von vertrauenswürdigen Typen auch clientseitiges DOM-basiertes XSS verhindern.
Einführung in das API
Bei vertrauenswürdigen Typen werden die folgenden riskanten Senkenfunktionen gesperrt. Einige davon kennen Sie vielleicht schon, da Browseranbieter und Web-Frameworks Sie aus Sicherheitsgründen bereits von der Verwendung dieser Funktionen abhalten.
- Skriptbearbeitung:
<script src>
und Festlegen von Textinhalten von<script>
-Elementen. - HTML aus einem String generieren:
- Plug-in-Inhalt ausführen:
- Laufzeit-JavaScript-Codekompilierung:
eval
setTimeout
setInterval
new Function()
Bei vertrauenswürdigen Typen müssen Sie die Daten verarbeiten, bevor sie an diese Senkenfunktionen übergeben werden. Die Verwendung nur eines Strings schlägt fehl, da der Browser nicht weiß, ob die Daten vertrauenswürdig sind:
anElement.innerHTML = location.href;
Erstellen Sie ein spezielles Objekt, einen vertrauenswürdigen Typ, um anzugeben, dass die Daten sicher verarbeitet wurden.
anElement.innerHTML = aTrustedHTML;
Durch vertrauenswürdige Typen wird die DOM-XSS-Angriffsfläche Ihrer Anwendung erheblich reduziert. Sie vereinfacht Sicherheitsüberprüfungen und ermöglicht 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 von Verstößen gegen die Content Security Policy vorbereiten
Sie können einen Report Collector wie den Open-Source-Dienst reporting-api-processor oder go-csp-collector bereitstellen oder eine der kommerziellen Entsprechungen verwenden. Sie können mit ReportingObserver auch benutzerdefiniertes Logging hinzufügen und Verstöße im Browser beheben:
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
oder durch Hinzufügen eines Event-Listeners:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
CSP-Header nur für Berichte hinzufügen
Fügen Sie den Dokumenten, die Sie zu vertrauenswürdigen Typen migrieren möchten, 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 problematischen Code entfernen, eine Bibliothek verwenden, eine Richtlinie für vertrauenswürdige Typen erstellen oder als letzte Möglichkeit eine Standardrichtlinie erstellen.
Umformulieren des anstößigen Codes
Möglicherweise wird der nicht konforme Code nicht mehr benötigt oder kann ohne die Funktionen, die die Verstöße verursachen, neu geschrieben werden:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
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, sodass der Browser keinen Verstoß generiert.
Richtlinie für vertrauenswürdige Typen erstellen
Manchmal können Sie den Code, der den Verstoß verursacht, nicht entfernen und es gibt keine Bibliothek, 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 Typobjekt erstellen.
Erstellen Sie zuerst eine Richtlinie. Richtlinien sind Factorys für vertrauenswürdige Typen, die bestimmte Sicherheitsregeln für die Eingabe erzwingen:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Mit diesem Code wird eine Richtlinie mit dem Namen myEscapePolicy
erstellt, die TrustedHTML
-Objekte mithilfe ihrer createHTML()
-Funktion erstellen kann. Die definierten Regeln enthalten HTML-Escape-Zeichen (<
), um die Erstellung neuer HTML-Elemente zu verhindern.
Verwenden Sie die Richtlinie wie folgt:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Standardrichtlinie verwenden
Manchmal können Sie den problematischen Code nicht ändern, z. B. wenn Sie eine Bibliothek eines Drittanbieters 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 einen vertrauenswürdigen Typ akzeptiert.
Zur Durchsetzung 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 diesen noch weiter sperren, indem Sie die Richtlinienerstellung einschränken.