Ziel der neuen Sanitizer API ist es, einen robusten Prozessor zu entwickeln, mit dem beliebige Strings sicher in eine Seite eingefügt werden können.
Anwendungen arbeiten ständig mit nicht vertrauenswürdigen Zeichenfolgen, aber das sichere Rendern dieser Inhalte als Teil eines HTML-Dokuments kann eine Herausforderung sein. Ohne ausreichende Sorgfalt kann es leicht passieren, dass Sie versehentlich Cross-Site-Scripting (XSS) einsetzen, die Angreifer ausnutzen könnten.
Um dieses Risiko zu minimieren, zielt der neue Vorschlag der Sanitizer API darauf ab, einen robusten Prozessor zu entwickeln, mit dem beliebige Strings sicher in eine Seite eingefügt werden können. In diesem Artikel wird die API vorgestellt und ihre Verwendung erläutert.
// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerror=alert(0)>`, new Sanitizer())
Nutzereingabe mit Escapezeichen versehen
Wenn Sie Nutzereingaben, Abfragestrings, Cookie-Inhalte usw. in das DOM einfügen, müssen die Strings mit den korrekten Escapezeichen versehen werden. Besonderes Augenmerk sollte auf die DOM-Manipulation über .innerHTML
gelegt werden, wobei Strings ohne Escape-Zeichen eine typische Quelle von XSS sind.
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.innerHTML = user_input
Wenn Sie HTML-Sonderzeichen im obigen Eingabestring maskieren oder mit .textContent
erweitern, wird alert(0)
nicht ausgeführt. Da das vom Nutzer hinzugefügte <em>
jedoch auch als String erweitert wird, kann diese Methode nicht verwendet werden, um die Textdekoration in HTML beizubehalten.
In diesem Fall solltest du nicht entkommen, sondern bereinigen.
Nutzereingabe bereinigen
Der Unterschied zwischen Escaping und Bereinigung
Beim Escaping werden spezielle HTML-Zeichen durch HTML-Entitäten ersetzt.
„Bereinigung“ bezieht sich auf das Entfernen semantisch schädlicher Teile (z. B. Skriptausführung) aus HTML-Strings.
Beispiel
Im vorherigen Beispiel führt <img onerror>
dazu, dass der Fehler-Handler ausgeführt wird. Wenn der onerror
-Handler jedoch entfernt würde, wäre es möglich, ihn im DOM sicher zu erweitern, während <em>
intakt bleibt.
// XSS 🧨
$div.innerHTML = `<em>hello world</em><img src="" onerror=alert(0)>`
// Sanitized ⛑
$div.innerHTML = `<em>hello world</em><img src="">`
Für eine korrekte Bereinigung muss der Eingabestring als HTML geparst, Tags und Attribute, die als schädlich eingestuft werden, ausgelassen und die harmlosen Tags beibehalten werden.
Die vorgeschlagene Sanitizer API-Spezifikation zielt darauf ab, eine solche Verarbeitung als Standard-API für Browser bereitzustellen.
Sanitizer API
Die Sanitizer API wird so verwendet:
const $div = document.querySelector('div')
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.setHTML(user_input, { sanitizer: new Sanitizer() }) // <div><em>hello world</em><img src=""></div>
{ sanitizer: new Sanitizer() }
ist jedoch das Standardargument. Es kann also wie unten dargestellt aussehen.
$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>
Hinweis: setHTML()
ist auf Element
definiert. Da es sich um eine Element
-Methode handelt, ist der zu parsende Kontext selbsterklärend (in diesem Fall <div>
). Das Parsen erfolgt einmal intern und das Ergebnis wird direkt in das DOM erweitert.
Wenn Sie das Ergebnis der Bereinigung als String abrufen möchten, können Sie .innerHTML
aus den setHTML()
-Ergebnissen verwenden.
const $div = document.createElement('div')
$div.setHTML(user_input)
$div.innerHTML // <em>hello world</em><img src="">
Über Konfiguration anpassen
Die Sanitizer API ist standardmäßig so konfiguriert, dass Strings entfernt werden, die die Skriptausführung auslösen. Über ein Konfigurationsobjekt können Sie jedoch auch eigene Anpassungen am Bereinigungsprozess vornehmen.
const config = {
allowElements: [],
blockElements: [],
dropElements: [],
allowAttributes: {},
dropAttributes: {},
allowCustomElements: true,
allowComments: true
};
// sanitized result is customized by configuration
new Sanitizer(config)
Die folgenden Optionen geben an, wie das Bereinigungsergebnis das angegebene Element behandeln soll.
allowElements
: Namen der Elemente, die vom Desinfektionsmittel beibehalten werden sollen.
blockElements
: Namen der Elemente, die mit dem Desinfektionsmittel entfernt werden sollen, ohne dass die Kinder davon betroffen sind.
dropElements
: Namen der Elemente, die das Desinfektionsmittel entfernen soll, zusammen mit den Kindern.
const str = `hello <b><i>world</i></b>`
$div.setHTML(str)
// <div>hello <b><i>world</i></b></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: [ "b" ]}) })
// <div>hello <b>world</b></div>
$div.setHTML(str, { sanitizer: new Sanitizer({blockElements: [ "b" ]}) })
// <div>hello <i>world</i></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: []}) })
// <div>hello world</div>
Mit den folgenden Optionen können Sie auch steuern, ob der Sanitizer bestimmte Attribute zulässt oder ablehnt:
allowAttributes
dropAttributes
Die Properties allowAttributes
und dropAttributes
erwarten Attributabgleichslisten, also Objekte, deren Schlüssel Attributnamen sind, und Werte sind Listen mit Zielelementen oder den Platzhalter *
.
const str = `<span id=foo class=bar style="color: red">hello</span>`
$div.setHTML(str)
// <div><span id="foo" class="bar" style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["span"]}}) })
// <div><span style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["p"]}}) })
// <div><span>hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["*"]}}) })
// <div><span style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({dropAttributes: {"id": ["span"]}}) })
// <div><span class="bar" style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {}}) })
// <div>hello</div>
Mit allowCustomElements
können Sie benutzerdefinierte Elemente zulassen oder ablehnen. Sofern zulässig, gelten weiterhin andere Konfigurationen für Elemente und Attribute.
const str = `<custom-elem>hello</custom-elem>`
$div.setHTML(str)
// <div></div>
const sanitizer = new Sanitizer({
allowCustomElements: true,
allowElements: ["div", "custom-elem"]
})
$div.setHTML(str, { sanitizer })
// <div><custom-elem>hello</custom-elem></div>
API-Oberfläche
Vergleich mit DomPurify
DOMPurify ist eine bekannte Bibliothek, die Bereinigungsfunktionen bietet. Der Hauptunterschied zwischen der Sanitizer API und DOMPurify besteht darin, dass DOMPurify das Ergebnis der Bereinigung als String zurückgibt, den Sie mit .innerHTML
in ein DOM-Element schreiben müssen.
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
const sanitized = DOMPurify.sanitize(user_input)
$div.innerHTML = sanitized
// `<em>hello world</em><img src="">`
DOMPurify kann als Fallback dienen, wenn die Sanitizer API nicht im Browser implementiert ist.
Die DOMPurify-Implementierung hat einige Nachteile. Wenn ein String zurückgegeben wird, wird der Eingabestring zweimal von DOMPurify und .innerHTML
geparst. Dieses doppelte Parsen verschwendet Verarbeitungszeit, kann aber auch zu interessanten Sicherheitslücken führen, die durch Fälle entstehen, in denen das Ergebnis des zweiten Parsings vom ersten abweicht.
Für HTML muss auch Kontext geparst werden. Beispielsweise ist <td>
in <table>
sinnvoll, aber nicht in <div>
. Da DOMPurify.sanitize()
nur einen String als Argument annimmt, musste der Parsing-Kontext erraten werden.
Die Sanitizer API verbessert den DOMPurify-Ansatz und wurde entwickelt, um die Notwendigkeit eines doppelten Parsens zu eliminieren und den Parsing-Kontext klarzustellen.
API-Status und Browserunterstützung
Die Sanitizer API wird im Rahmen des Standardisierungsprozesses diskutiert und Chrome implementiert sie derzeit.
Schritt | Status |
---|---|
1. Erklärende Mitteilung erstellen | Abschließen |
2. Spezifikationsentwurf erstellen | Abschließen |
3. Feedback einholen und Design iterieren | Abschließen |
4. Chrome-Ursprungstest | Abschließen |
5. Starten | Intent to Ship on M105 |
Mozilla: lohnt es sich, ein Prototyping für diesen Vorschlag zu erstellen und setzt ihn aktiv um.
WebKit: Die Antwort finden Sie in der WebKit-Mailingliste.
Sanitizer API aktivieren
Unterstützte Browser
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Aktivierung über die Option about://flags
oder die Befehlszeile
Chrome
Chrome implementiert derzeit die Sanitizer API. In Chrome 93 oder höher können Sie das Verhalten testen, indem Sie das Flag about://flags/#enable-experimental-web-platform-features
aktivieren. In früheren Versionen der Chrome Canary- und Entwicklerversion können Sie sie über --enable-blink-features=SanitizerAPI
aktivieren und gleich ausprobieren. Hier finden Sie eine Anleitung dazu, wie Sie Chrome mit Flags ausführen.
Firefox
Firefox implementiert auch die Sanitizer API als experimentelle Funktion. Setzen Sie zum Aktivieren das Flag dom.security.sanitizer.enabled
in about:config
auf true
.
Funktionserkennung
if (window.Sanitizer) {
// Sanitizer API is enabled
}
Feedback
Wir würden uns freuen, wenn Sie diese API ausprobieren und uns Feedback geben. Teilen Sie uns Ihre Meinung zu GitHub-Problemen mit der Sanitizer API mit und diskutieren Sie mit den Autoren der Spezifikationen sowie mit Personen, die an dieser API interessiert sind.
Wenn Sie Fehler oder unerwartetes Verhalten bei der Chrome-Implementierung feststellen, melden Sie den Fehler und melden Sie ihn. Wählen Sie die Blink>SecurityFeature>SanitizerAPI
-Komponenten aus und teilen Sie Details mit, damit Implementierer das Problem verfolgen können.
Demo
Wenn Sie die Sanitizer API in Aktion sehen möchten, sehen Sie sich den Sanitizer API Playground von Mike West an:
Verweise
- HTML Sanitizer API-Spezifikation
- Repository „WICG/sanitizer-api“
- Häufig gestellte Fragen zur Sanitizer API
- HTML Sanitizer API-Referenzdokumentation zu MDN
Foto von Towfiqu barbhuiya auf Unsplash