DOM-basiertes Cross-Site-Scripting (DOM XSS) tritt auf, wenn Daten aus einer vom Nutzer gesteuerten Quelle (z. B. ein Nutzername oder eine Weiterleitungs-URL aus dem URL-Fragment) einen Senke erreichen. Dies ist eine Funktion wie eval()
oder ein Eigenschafts-Setter wie .innerHTML
, mit dem beliebiger JavaScript-Code ausgeführt werden kann.
DOM-XSS ist eine der häufigsten Sicherheitslücken in der Websicherheit. Entwicklerteams führen sie häufig versehentlich in ihre Apps ein. Vertrauenswürdige Typen bieten Ihnen die Tools, um Anwendungen zu schreiben, zu prüfen und frei von DOM-XSS-Sicherheitslücken zu halten, indem gefährliche Web-API-Funktionen standardmäßig sicher gemacht werden. Vertraute 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 websiteübergreifendem Scripting. Einige XSS-Sicherheitslücken werden durch serverseitigen Code verursacht, der den HTML-Code für die Website auf unsichere Weise erstellt. Andere haben eine Grundursache auf dem Client, bei der der JavaScript-Code gefährliche Funktionen mit vom Nutzer kontrollierten Inhalten aufruft.
Um serverseitiges XSS zu verhindern, sollten Sie HTML nicht durch Zusammenführen von Strings generieren. Verwenden Sie stattdessen sichere Vorlagenbibliotheken mit automatischem Kontext-Escaping und eine noncebasierte Content Security Policy, um Bugs weiter zu minimieren.
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 unsicheren Sink-Funktionen gesperrt. Einige davon kennen Sie vielleicht schon, da Browseranbieter und Web-Frameworks Sie aus Sicherheitsgründen bereits von der Verwendung dieser Funktionen abraten.
- Script-Manipulation:
<script src>
und Textinhalte von Elementen festlegen<script>
. - HTML aus einem String generieren:
- Plugin-Inhalte ausführen:
- Laufzeitkompilierung von JavaScript-Code:
eval
setTimeout
setInterval
new Function()
Bei vertrauenswürdigen Typen müssen Sie die Daten verarbeiten, bevor Sie sie an diese Senkenfunktionen übergeben. 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;
Mit vertrauenswürdigen Typen lässt sich die Angriffsfläche für DOM-XSS-Angriffe Ihrer Anwendung erheblich reduzieren. Es vereinfacht die Sicherheitsüberprüfungen und ermöglicht es Ihnen, die typbasierten Sicherheitsprüfungen zu erzwingen, die beim Kompilieren, Linieren oder Bündeln Ihres Codes zur Laufzeit im Browser ausgeführt werden.
Vertraute Typen verwenden
Auf Berichte zu Verstößen gegen die Content Security Policy vorbereiten
Sie können einen Berichts-Collector wie den Open-Source-Dienst reporting-api-processor oder go-csp-collector bereitstellen oder eines der kommerziellen Äquivalente verwenden. Mit einem ReportingObserver können Sie auch benutzerdefinierte Protokolle und Fehlerbehebungen im Browser hinzufügen:
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 indem Sie einen Ereignis-Listener hinzufügen:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
CSP-Header nur für Berichte hinzufügen
Fügen Sie den folgenden HTTP-Antwortheader zu Dokumenten hinzu, die Sie zu vertrauenswürdigen Typen migrieren möchten:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Alle Verstöße werden jetzt an //my-csp-endpoint.example
gemeldet, die Website funktioniert aber weiterhin. Im nächsten Abschnitt wird die Funktionsweise von //my-csp-endpoint.example
erläutert.
Verstöße gegen vertrauenswürdige Typen identifizieren
Ab sofort sendet der Browser jedes Mal, wenn Trusted Types einen Verstoß erkennt, 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"
}
}
Das bedeutet, dass in https://my.url.example/script.js
in Zeile 39 innerHTML
mit der Zeichenfolge aufgerufen wurde, die mit <img src=x
beginnt. Anhand dieser Informationen können Sie eingrenzen, welche Codeteile möglicherweise DOM-XSS verursachen und geändert werden müssen.
Verstöße beheben
Es gibt mehrere Möglichkeiten, einen Verstoß gegen vertrauenswürdige Typen 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.
Den fehlerhaften Code umschreiben
Möglicherweise ist der nicht konforme Code nicht mehr erforderlich oder kann ohne die Funktionen umgeschrieben werden, die die Verstöße verursachen:
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-Nutzlast 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, mit der der Wert bereinigt und ein vertrauenswürdiger Typ für Sie erstellt werden kann. In diesen Fällen können Sie ein Objekt vom Typ „Vertrauenswürdiger Typ“ selbst erstellen.
Erstellen Sie zuerst eine Richtlinie. Richtlinien sind Fabriken für vertrauenswürdige Typen, die bestimmte Sicherheitsregeln auf ihre Eingaben anwenden:
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 führen zu einem HTML-Escape von <
-Zeichen, um das Erstellen neuer HTML-Elemente zu verhindern.
Verwenden Sie die Richtlinie so:
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 fehlerhaften 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 überall dort verwendet, wo ein String in einem Sink verwendet wird, der nur vertrauenswürdige Typen akzeptiert.
Zur Durchsetzung der Content Security Policy wechseln
Wenn Ihre Anwendung keine Verstöße mehr verursacht, können Sie vertrauenswürdige Typen 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.