HTML verwendet dieselben Textzeichen für Inhalt und Struktur. Ein Kleiner-als-Zeichen kann Teil einer mathematischen Aussage sein oder ein Tag beginnen. Ein Ampersand kann sichtbar gemeint sein oder eine Zeichenreferenz einleiten. HTML-Entities, genauer Character References, geben dem Dokument eine eindeutige Möglichkeit, solche Zeichen als Inhalt darzustellen, ohne dass der Parser sie als Markup interpretiert.

Markup und Text teilen sich denselben Kanal

Der Browser liest Tags, Attribute, Kommentare und Text aus einer einzigen Zeichenfolge. Im Textkontext signalisiert < potenzielles Markup und & den Beginn einer Referenz. Die Schreibweisen &lt; und &amp; führen im DOM zu den sichtbaren Zeichen.

Named Entities wie &copy; können Absicht lesbar ausdrücken. Numerische Referenzen bezeichnen einen Unicode-Codepoint. In einem UTF-8-Dokument dürfen die meisten Zeichen direkt stehen; Referenzen bleiben dort wichtig, wo Syntax sonst missverstanden würde.

Escaping hängt vom Zielkontext ab

Text zwischen zwei Tags folgt anderen Regeln als ein Wert in einem Attribut. Innerhalb eines in Anführungszeichen gesetzten Attributs werden zusätzlich die passenden Quotes bedeutend. JavaScript, CSS und URLs besitzen wiederum eigene Grammatiken.

Eine generische Funktion namens „escape“ kann diese Unterschiede nicht automatisch lösen. Template-Systeme sollten den Ausgabekontext kennen und sicher interpolieren. Untrusted Data direkt in Scriptblöcke oder Eventhandler einzusetzen ist selbst mit Escaping unnötig riskant.

Encoding und Sanitizing erfüllen verschiedene Aufgaben

Encoding sorgt dafür, dass eingegebener Text als Text erscheint. Tippt ein Nutzer <strong>, zeigt korrektes Escaping die Zeichen statt eines Elements. Sanitizing wird benötigt, wenn eine Anwendung bewusst einen begrenzten Teil von HTML zulässt.

Ein Sanitizer parst Markup und entfernt nicht erlaubte Elemente, Attribute und URL-Schemas. Regex und Stringersetzung reichen dafür nicht, weil Browser fehlerhaftes HTML nach komplexen Regeln reparieren.

Double Encoding macht Verantwortungsduplikate sichtbar

Wird ein bereits codiertes Ampersand erneut escaped, entsteht aus &lt; die Quelle &amp;lt;. Der Browser zeigt anschließend den Entity-Text statt des gewünschten Zeichens. Häufig haben Backend und Template beide angenommen, für HTML-Ausgabe verantwortlich zu sein.

Gewöhnlicher Inhalt sollte als semantischer Unicode-Text gespeichert und erst am finalen Rendering-Boundary escaped werden. Präsentationscodierung ist selten ein guter kanonischer Datenbankwert.

Eine Referenz verändert nicht die Unicode-Bedeutung

Ein direktes UTF-8-Zeichen, eine dezimale Referenz, eine hexadezimale Referenz und eine Named Entity können im DOM dasselbe Zeichen ergeben. Die Source Strings unterscheiden sich, der geparste Text nicht. Vergleiche auf Source-Ebene müssen das berücksichtigen.

Unicode-Normalisierung ist ein separates Thema. Zwei visuell gleiche Zeichenfolgen können aus verschiedenen Codepoint-Sequenzen bestehen. HTML-Entities normalisieren diese Unterschiede nicht automatisch.

Source, DOM und sichtbarer Text sind verschiedene Ebenen

„View Source“ zeigt, was der Server geliefert hat. Der DOM-Inspector zeigt die Struktur nach HTML-Parsing, Fehlerkorrektur und möglichen JavaScript-Änderungen. Kopierter oder gelesener Text zeigt wiederum das Ergebnis für den Nutzer.

Beim Debugging einer sichtbaren Entity muss klar sein, auf welcher Ebene sie existiert. Ein &amp; in der Source kann als normales Ampersand im DOM-Text erscheinen; dieselbe Zeichenfolge über textContent wird anders behandelt.

Browser sind tolerant, Sicherheitsfilter dürfen es nicht raten

HTML besitzt umfangreiche Error-Recovery. Fehlende Endtags, unvollständige Referenzen und ungewöhnliche Verschachtelung werden oft trotzdem zu einem DOM verarbeitet. Ein einfacher Filter kann eine andere Struktur erwarten als der Browser.

Gepflegte Encoder und Sanitizer orientieren sich an tatsächlichen Parsingregeln. Security-Tests sollten den resultierenden DOM und nicht nur einen Stringausschnitt der Response prüfen.

Templates müssen den sicheren Weg zum Standard machen

Ordinary Interpolation sollte automatisch escaped werden. Raw-HTML-Helper sind Ausnahmen, die im Code Review eine klare Herkunft und Sanitization nachweisen müssen. Dadurch hängt Sicherheit nicht von einer vergessenen Funktion in jedem Template ab.

Gefährlich sind Werte, die bereits vorher mit Markup zusammengesetzt wurden. Der beste Auto-Escaper kann die ursprüngliche Grenze zwischen Daten und Code dann nicht mehr rekonstruieren.

UTF-8 reduziert unnötige Referenzen

Historisch halfen Entities, Symbole in begrenzten Zeichensätzen darzustellen. Moderne Dokumente können Buchstaben, Akzente und nicht lateinische Schriften direkt als UTF-8 speichern. Das macht Source und redaktionelle Arbeit lesbarer.

Nicht jedes Nicht-ASCII-Zeichen muss automatisch umgewandelt werden. Eine sinnvolle Konvention escaped syntaktisch relevante Zeichen und verwendet Referenzen dort, wo sie Quellcode verständlicher oder transportstabiler machen.

Plain Text und Sanitized HTML sollten verschiedene Typen sein

Wenn beide als beliebige Strings durch das System laufen, müssen Entwickler raten, ob Raw Rendering erlaubt ist. Feldnamen wie body_html, Schemas und Wrapper-Typen machen Herkunft und erlaubte Operationen sichtbar.

APIs sollten ebenfalls erklären, ob ein Feld Text oder Markup enthält. Anhand vorhandener Tags lässt sich der Typ nicht zuverlässig erkennen; Text kann absichtlich Winkelklammern enthalten.

Validierung ersetzt keinen Rendering-Test

Ein HTML-Validator findet viele strukturelle Fehler, modelliert aber nicht jede Browser- und Scriptinteraktion. Komponenten mit Legacy-Inhalt oder Third-Party-Markup sollten in echten Browsern geprüft werden, besonders wenn Sanitization beteiligt ist.

Das endgültige DOM ist die Struktur, mit der CSS, JavaScript und Accessibility APIs arbeiten. Seine Sicherheit und Bedeutung zählen mehr als eine oberflächlich plausible Source.

Entities sind ein Werkzeug der Grammatik

Sie verschlüsseln nichts und verbergen keine Informationen. Sie erlauben, ein Zeichen als Daten in einer Sprache auszudrücken, in der dasselbe Zeichen Syntax einleiten könnte.

Die robuste Regel ist einfach: semantischen Text bewahren, passend zum exakten Outputkontext escapen und nur bewusst erlaubtes HTML sanitizen. Diese klare Verantwortung verhindert sichtbares Double Encoding und einen wichtigen Teil von Injection-Schwachstellen.