Kritisch

DOMPurify SAFE_FOR_TEMPLATES-Bypass: Aufgeteilte Template-Ausdrücke verbleiben in Inhalten angehängter Shadow-Roots in DOM-Ausgabemodi

  • Veröffentlicht: 22.10.2026
  • Aktualisiert: 23.10.2026 00:05

Zusammenfassung

Wenn DOMPurify mit SAFE_FOR_TEMPLATES: true zusammen mit einem DOM-Rückgabemodus (RETURN_DOM: true, RETURN_DOM_FRAGMENT: true oder IN_PLACE: true) konfiguriert ist, überstehen Template-Ausdrücke wie {{...}}, ${...} und <%...%> die Bereinigung im Textinhalt einer angehängten Shadow-Root. Das nachgelagerte Sicherheitsnetz _scrubTemplateExpressions (src/purify.ts:1717) wurde zwar um eine Rekursion in <template>.content erweitert, rekursiert jedoch niemals in angehängte Shadow-Roots. Ein Angreifer, der einen DOM-Knoten mit einer Shadow-Root übergeben kann (die Oberflächen IN_PLACE und Shadow-fähige RETURN_DOM), kann einen Ausdruck über Textknoten aufteilen, die durch unzulässige Elemente getrennt sind; nach dem Entfernen dieser Elemente werden die Fragmente benachbart und serialisieren zu einem vollständigen Ausdruck, der von einer nachgelagerten Template-Engine ausgewertet wird.

Ursache

SAFE_FOR_TEMPLATES neutralisiert Template-Ausdrücke über zwei Mechanismen: eine Bereinigung pro Textknoten während des Hauptdurchlaufs (_stripTemplateExpressions, angewendet in _sanitizeElements bei src/purify.ts:2246) und einen abschließenden nachgelagerten Durchlauf (_scrubTemplateExpressions, src/purify.ts:1717), der benachbarte Textknoten normalisiert und Ausdrücke erneut entfernt, die erst nach dem Zusammenfügen entstehen. Die Bereinigung pro Knoten kann einen in einzelne Fragmente aufgeteilten Ausdruck nicht erfassen, da jedes einzelne Fragment die Trennzeichen-Regexes nicht erfüllt. Der nachgelagerte Durchlauf ist der einzige Mechanismus, der die zusammengefügte Form abfangen würde, kann jedoch strukturell den Inhalt von Shadow-Roots nicht erreichen.

_scrubTemplateExpressions (src/purify.ts:1717-1751):
  - node.normalize() at line 1718 does not merge text nodes inside shadow roots (DOM spec).
  - NodeIterator at lines 1723-1731 does not descend into shadow trees (DOM spec).
  - Explicit recursion at lines 1743-1750 enters <template>.content only, via
    querySelectorAll('template'); there is no equivalent recursion into attached
    shadow roots.
Shadow roots are sanitized earlier by _sanitizeShadowDOM (src/purify.ts:2710), which
applies the per-node strip but never performs the post-walk normalize-and-rescrub.

Textknoten in Shadow-Roots werden daher nur einzeln bereinigt und nie als zusammengeführte Einheit erneut geprüft.

Auswirkungen

Ein Angreifer, der DOM-Knoten-Eingaben mit einer angehängten Shadow-Root kontrolliert, kann Template-Ausdrücke einschleusen, die die Bereinigung überstehen und später von der Template-Engine der Anwendung ausgewertet werden. In clientseitigen Kontexten führt dies zu Cross-Site-Scripting (XSS); in einem serverseitigen Template-Kontext kann dies zu Template-Injection eskalieren. Eine Ausnutzung erfordert eine nicht standardmäßige Anwendungskonfiguration: Die Anwendung muss SAFE_FOR_TEMPLATES und einen DOM-Rückgabemodus aktivieren, einen DOM-Knoten übergeben, der bereits eine angehängte Shadow-Root besitzt, und den bereinigten Shadow-Root-Inhalt an einen Template-Evaluator weiterleiten. Die Shadow-Root muss nicht programmatisch mit attachShadow erstellt werden: In Engines, die deklaratives Shadow-DOM unterstützen, erzeugt vom Angreifer bereitgestelltes Markup mit <template shadowrootmode="open">, das über setHTMLUnsafe oder Document.parseHTMLUnsafe analysiert wird, dieselbe angehängte Shadow-Root, sodass eine reine HTML-Zeichenkette diese Lücke erreicht, sobald die Anwendung entsprechend konfiguriert ist. Die nachgewiesene Fähigkeit besteht in der Injektion eines vollständigen, vom Angreifer gewählten Ausdrucks in den Shadow-Root-Inhalt nach der Bereinigung; die resultierende Skriptausführung sowie etwaige Folgen für Vertraulichkeit oder Integrität hängen vom Evaluierungskontext der nachgelagerten Engine ab.

Proof of Concept

Preconditions:
  - DOMPurify built from the affected source (dist/purify.js or an ESM/CJS bundle).
  - A browser (or DOM implementation) with shadow DOM support.
  - Application constructs a DOM node with an attached shadow root before sanitizing,
    and later evaluates the shadow root's text content as a template.

Erstellen Sie ein Host-Element, dessen Shadow-Root einen Template-Ausdruck enthält, der über durch unzulässige Elemente getrennte Textknoten aufgeteilt ist, und bereinigen Sie ihn direkt mit SAFE_FOR_TEMPLATES:

js
const host = document.createElement('div');
const root = host.attachShadow({ mode: 'open' });

// Split "${alert(document.domain)}" so no single fragment matches a regex.
// <object> is not in the default ALLOWED_TAGS and is removed during sanitization,
// leaving the surrounding text-node siblings adjacent.
root.appendChild(document.createTextNode('$'));
root.appendChild(document.createElement('object'));
root.appendChild(document.createTextNode('{alert(document.domain)}'));

DOMPurify.sanitize(host, { IN_PLACE: true, SAFE_FOR_TEMPLATES: true });

// After the disallowed element is removed, the fragments are adjacent and survive.
host.shadowRoot.normalize();
console.log(host.shadowRoot.textContent);
${alert(document.domain)}

Ein nachgelagerter Template-Evaluator, der shadowRoot.textContent liest (beispielsweise new Function('return ' + content + '')() oder eine beliebige ${...}/{{...}}-Engine), wertet anschließend den vollständig zusammengesetzten Ausdruck aus.

Angriffskette

  1. Exposition: Die Anwendung ruft DOMPurify.sanitize(node, cfg) (src/purify.ts:2854) mit SAFE_FOR_TEMPLATES: true und IN_PLACE: true (oder einem Shadow-fähigen RETURN_DOM) auf und übergibt einen DOM-Knoten, der bereits eine angehängte Shadow-Root trägt.
  2. Kontrolle: Der Angreifer kontrolliert die Textknoten der Shadow-Root und die unzulässigen Trennelemente dazwischen; jedes Fragment ($, {alert(document.domain)}) ist ein gültiger, für sich allein harmloser Textknoten.
  3. Pfad: _sanitizeAttachedShadowRoots (src/purify.ts:2977) leitet die Shadow-Root an _sanitizeShadowDOM weiter (src/purify.ts:2710), wodurch Ausdrücke pro Textknoten bereinigt werden (src/purify.ts:2246) und die unzulässigen Trennelemente entfernt werden, sodass die Fragmente nebeneinander zu liegen kommen.
  4. Schutzmechanismus: Das nachgelagerte Sicherheitsnetz _scrubTemplateExpressions (src/purify.ts:3131-3132) ist der einzige Durchlauf, der den zusammengefügten Ausdruck abfangen würde, rekursiert jedoch niemals in Shadow-Roots (src/purify.ts:1717-1751 rekursiert nur in <template>.content).
  5. Primitiv: Die benachbarten Fragmente serialisieren als eine einzige verkettete Zeichenkette über shadowRoot.textContent / shadowRoot.innerHTML, wodurch der vollständige Ausdruck wiederhergestellt wird.
  6. Ergebnis: Eine nachgelagerte Template-Engine wertet den vom Angreifer gewählten Ausdruck aus und erzielt eine Skriptausführung (oder Template-Injection) im Kontext der Engine.

Nachweis des Bypasses

Der relevante Schutzmechanismus ist SAFE_FOR_TEMPLATES, insbesondere der nachgelagerte Durchlauf, der vor Ausdrücken schützt, die erst nach dem Zusammenfügen benachbarter Textknoten entstehen. Diese Abwehr wurde zuvor unter CVE-2026-65900 (GHSA-gvmj-g25r-r7wr) auf <template>.content erweitert, indem eine Rekursion für querySelectorAll('template') bei src/purify.ts:1743-1750 hinzugefügt wurde. Die entscheidende Frage ist, ob irgendein Codepfad die entsprechende Zusammenführungs- und Nachbereinigungsoperation innerhalb von Shadow-Roots ausführt. Eine Überprüfung aller Aufrufstellen von _scrubTemplateExpressions (src/purify.ts:1717, 1747, 3132, 3141) zeigt, dass keine eine Shadow-Root empfängt, und _sanitizeShadowDOM (src/purify.ts:2710) ruft niemals _scrubTemplateExpressions oder normalize() auf. Die Bereinigung pro Knoten kann dies nicht kompensieren: MUSTACHE_EXPR (src/regexp.ts:3) erfordert {{ oder }}, TMPLIT_EXPR (src/regexp.ts:5) erfordert ${, und ERB_EXPR (src/regexp.ts:4) erfordert <% oder %>, von denen keines in einem Einzelzeichenfragment vorkommt. Die Regressionstests von CVE-2026-65900 testen ausschließlich <template>.content; kein Test deckt Inhalte von Shadow-Roots unter SAFE_FOR_TEMPLATES ab. Die Lücke ist daher ein unbehobener Fall an einer eigenständigen Bereinigungsgrenze (Shadow-Roots) und nicht eine Umgehung des bereitgestellten Template-Content-Fixes.

Betroffene Versionen

  • Ökosystem: npm
  • Paket: dompurify
  • Bestätigter betroffener Bereich: >= 3.4.4, <= 3.4.16
  • Zuletzt geprüfte Version: 3.4.16 (npm registry)))
  • Behebungsstatus: nicht behoben

Die untere Grenze 3.4.4 ist die Version, mit der sowohl der Bereinigungsdurchlauf für angehängte Shadow-Roots als auch das Sicherheitsnetz _scrubTemplateExpressions eingeführt wurden; die Versionen 3.4.4 bis 3.4.7 enthalten das Sicherheitsnetz ohne Rekursion für Inhaltscontainer, und ab 3.4.8 wurde eine Rekursion für <template>.content hinzugefügt, wobei eine Rekursion in Shadow-Roots weiterhin fehlt. Keine veröffentlichte Version und keine aktuelle Entwicklungsversion enthält eine Behebung für Shadow-Roots.

Empfohlene Behebung

Setzen Sie dieselbe nachgelagerte Invariante zum Normalisieren und erneuten Bereinigen für angehängte Shadow-Roots durch, die bereits für <template>.content gilt. Nachdem _sanitizeShadowDOM das Durchlaufen einer Shadow-Root abgeschlossen hat, führen Sie _scrubTemplateExpressions über diese Shadow-Root aus, damit durch das Entfernen von Elementen entstandene benachbarte Textknoten zusammengeführt und erneut bereinigt werden; alternativ kann _scrubTemplateExpressions so angepasst werden, dass angehängte Shadow-Roots wie Template-Inhalte erkannt und rekursiv verarbeitet werden. Die Behebung muss verschachtelte Shadow-Roots und Shadow-Roots innerhalb von <template>.content abdecken sowie alle drei Ausdrucksdialekte verarbeiten, da jedes unzulässige Element als Trennzeichen dienen kann, das benachbarte Fragmente erzeugt.


Gemeldet von zx (GitHub: @manus-pi).

Was zu tun ist

Eine Behebung ist nicht festgelegt.

Betroffene Software

Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.

PlattformPaketAnfällige VersionenBehoben in
npm-Paket dompurify >= 3.4.4, < 3.4.17 3.4.17

Verweise

CVSS-Wert -
CVSS-Vektor
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N
Schwäche (CWE)
CWE-79
Plattform
npm
Paket
dompurify
Veröffentlicht
22.10.2026
Aktualisiert
23.10.2026 00:05

Quellen und Lizenzen · Von DockRay bearbeitet oder übersetzt; Rechte und Lizenzen der Quellen bleiben erhalten.

OSV / GitHub Advisory Database - GHSA-cg2g-xh64-v4q7 - CC BY 4.0

Schwachstellenhistorie: dompurify

Alle veröffentlichten Schwachstellen dieses Pakets (npm-Paket), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.

Alle (32) im Archiv
Einträge insgesamt
32
Erste → letzte
08.2020 10.2026
Ohne Fix
1
In Angriffen ausgenutzt
0

Verteilung nach Schweregrad

  • Kritisch 3
  • Hoch 3
  • Mittel 19
  • Niedrig 7

Veröffentlichungen pro Jahr

  1. 2020: 3
  2. 2021: 0
  3. 2022: 0
  4. 2023: 1
  5. 2024: 3
  6. 2025: 1
  7. 2026: 24
Schweregrad:
  1. DOMPurify SAFE_FOR_TEMPLATES-Bypass: Aufgeteilte Template-Ausdrücke verbleiben in Inhalten angehängter Shadow-Roots in DOM-Ausgabemodi dieser Eintrag
    Kritisch ghsa-cg2g-xh64-v4q7 >= 3.4.4, < 3.4.17 3.4.17
  2. Hoch · 8.2 CVE-2026-47423 >= 3.4.4, < 3.4.5 3.4.5
  3. Kritisch · 9.1 CVE-2024-48910 < 2.4.2 2.4.2
  4. Hoch · 10.0 CVE-2024-47875 < 2.5.0, >= 3.0.0, < 3.1.3 2.5.0, 3.1.3
  5. Hoch · 7.0 CVE-2024-45801 < 2.5.4, >= 3.0.0, < 3.1.3 2.5.4, 3.1.3
  6. Kritisch ghsa-mjjq-c88q-qhr6 < 2.0.7 2.0.7

Erkennen Sie Schwachstellen in Ihrer Anwendung

Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.

Anwendung verbinden
DockRay Newsletter

Anwendungssicherheit in Ihrem Postfach

Wichtige Schwachstellen, konkrete Empfehlungen und praktische Tipps für Ihre Anwendungen. Wählen Sie Ihre Technologien und verfolgen Sie, was für Ihr Projekt relevant ist.

  • Eine tägliche Zusammenfassung neuer Schwachstellen, auch kritischer.
  • Benachrichtigungen über wichtige Updates.
  • Praktische Tipps zur Sicherheit und Überwachung von Anwendungen.

Leer lassen, um Meldungen zu allen Technologien zu erhalten.

Kostenlos anmelden. In jeder E-Mail mit einem Klick abmelden.