Zum Inhalt springen

Webentwicklung

HTML-Entity-Kodierung und Decodierung: Der vollständige Entwicklerleitfaden

11 Min. Lesezeit

Ein Benutzer fügt einen Code-Schnipsel in ein Kommentarfeld ein, und plötzlich bricht das gesamte Layout der Seite zusammen. Die Ursache ist nicht exotisch — es ist ein einziges unescaped <-Zeichen, das als Beginn eines HTML-Tags interpretiert wird. Der Browser hört auf, den Kommentar als Text darzustellen, und sucht nach Elementnamen, Attributen und schließenden Klammern. Die Lösung ist ebenso einfach: Ersetzen Sie dieses Zeichen durch seine HTML-Entity, und die Seite wird korrekt gerendert. Dieser Leitfaden erklärt, was HTML-Entities sind, warum jeder Entwickler mit benutzergenerierten Inhalten sie braucht und wie man die häufigen Fehler vermeidet, die in der Produktion reale Probleme verursachen.

Was HTML-Entities wirklich sind

HTML reserviert eine kleine Menge von Zeichen für seine eigene Syntax: eckige Klammern definieren Tags, Ampersands leiten Entities ein und Anführungszeichen umschließen Attributwerte. Wenn Sie eines dieser Zeichen als reinen Text anzeigen müssen — nicht als Markup — ersetzen Sie es durch eine Entity-Referenz. Eine Entity-Referenz ist entweder ein kurzer Name zwischen Ampersand und Semikolon (&amp;lt;) oder ein numerischer Code (&#60; oder &#x3C;). Der Browser interpretiert die Entity beim Parsen und gibt das entsprechende Zeichen ohne Behandlung als strukturelles HTML aus.

Es gibt zwei Entity-Familien. Benannte Entities verwenden menschenlesbare Namen — &amp;amp; für Ampersand, &amp;lt; für Kleiner-als, &amp;gt; für Größer-als. Numerische (dezimale) Entities verwenden den Unicode-Codepoint des Zeichens: &#60; für Kleiner-als, &#x3C; in hexadezimal. Beide Ansätze erzeugen dasselbe Ergebnis im Browser; benannte Entities sind für Menschen lesbarer, numerische Entities decken jedes Unicode-Zeichen ab.

Die fünf Entities, die jeder Webentwickler auswendig kennen muss, sind: &amp;amp; (&), &amp;lt; (<), &amp;gt; (>), &amp;quot; ("), &amp;apos; ('). Die HTML5-Spezifikation definiert zudem &amp;nbsp; für ein Leerzeichen ohne Umbruch — die einzige benannte Entity, die nicht einem Syntaxzeichen entspricht, aber ständig im Webinhalt vorkommt.

Die fünf Pflicht-Entities und ihre numerischen Alternativen

Benannte EntityZeichenDezimalHexWann benötigt
&amp;amp;&&38;&#x26;Jeder Ampersand in Text oder Attributwerten — muss zuerst escaped werden
&amp;lt;<&#60;&#x3C;Jedes Kleiner-als-Zeichen außerhalb eines Tags; zeigt Code-Schnipsel, Vergleiche
&amp;gt;>&#62;&#x3E;Größer-als in Text; zusammen mit &lt; in Codeblöcken erforderlich
&amp;quot;"&#34;&#x22;Doppelte Anführungszeichen innerhalb von HTML-Attributwerten
&amp;apos;'&#39;&#x27;Einfache Anführungszeichen in einfach Anführungszeichen-Attributen; XML-Kompatibilität

Ampersands zuerst escapen — immer

Der Ampersand ist das Tor-Zeichen für alle Entities. Wenn Sie zuerst < escapen und den &amp; unescaped lassen, interpretiert der Browser Ihr neu erstelltes &amp;lt; möglicherweise als wörtlichen Ampersand gefolgt von "lt;" statt als Entity &amp;lt;. Kodieren Sie immer zuerst &amp; als &amp;amp;, bevor Sie etwas anderes antasten. Das ist der häufigste Kodierungsfehler im Produktionscode.

Warum das Escaping von Benutzereingaben eine Sicherheitsanforderung ist

Wenn eine Anwendung Text speichert, den ein Benutzer eingegeben hat, und ihn ohne Kodierung wieder in HTML ausgibt, werden alle eckigen Klammern in diesem Text Teil des DOM. Ein bösartiger Kommentar mit <script>alert('XSS')</script> wird nicht als Text dargestellt — er wird als JavaScript ausgeführt. Das ist ein Cross-Site-Scripting (XSS)-Angriff, einer der häufigsten Web-Schwachstellen. Das OWASP Top 10 hat XSS in jeder Ausgabe geführt, weil Entwickler immer noch vergessen, Benutzereingaben zu escapen.

Die Verteidigung ist unkompliziert: Bevor ein benutzergenerierter String in HTML-Ausgabe eingeht, kodieren Sie die fünf Sonderzeichen. Wenn der String <script> durch ordnungsgemäße Kodierung geht, wird er zu &amp;lt;script&amp;gt; und der Browser stellt ihn als harmlosen sichtbaren Text dar. Das gleiche Prinzip gilt für Attribute: Wenn der Benutzername ein Anführungszeichen enthält und Sie ihn ohne Kodierung in ein title-Attribut einfügen, kann der Angreifer das Attribut verlassen und beliebiges HTML injizieren.

<!-- GEFÄHRLICH: Benutzereingabe uncodiert injiziert -->
<div class="comment">${userComment}</div>

<!-- SICHER: Benutzereingabe vor der Injektion kodiert -->
<div class="comment">${encodeHtml(userComment)}</div>

<!-- GEFÄHRLICH: Name ohne Kodierung in Attribut -->
<img src="photo.jpg" alt="${userName}">

<!-- SICHER: Name innerhalb des Attributs kodiert -->
<img src="photo.jpg" alt="${encodeHtml(userName)}">

HTML-Kodierung vs. URL-Kodierung: sie lösen verschiedene Probleme

Entwickler verwechseln HTML-Kodierung häufig mit URL-Kodierung (auch Prozent-Kodierung genannt). Sie dienen völlig unterschiedlichen Zwecken. HTML-Kodierung schützt Textinhalte innerhalb eines HTML-Dokuments, damit Sonderzeichen dargestellt und nicht geparst werden. URL-Kodierung schützt Zeichen innerhalb eines URL-Pfads, einer Abfragezeichenfolge oder eines Fragments, damit die korrekte HTTP-Anfrage übertragen wird.

Ein Leerzeichen in HTML wird zu &amp;nbsp; (oder einfach einem normalen Leerzeichen, wenn kein Zeilenumbruch verhindert werden muss). Dasselbe Leerzeichen in einer URL wird zu %20. Der Ampersand in HTML wird zu &amp;amp; — aber in einer URL-Abfragezeichenfolge ist er ein Trennzeichen; wenn ein wörtlicher Ampersand in einem Parameterwert benötigt wird, wird er zu %26. Die falsche Kodierung im falschen Kontext bringt alles durcheinander: &amp;nbsp; innerhalb einer URL funktioniert nicht, und %20 innerhalb von HTML erscheint als wörtlicher Text.

Die Faustregel

Innerhalb von HTML-Text oder Attributen: HTML-Entities verwenden. Innerhalb von URLs, Dateipfaden oder Abfrageparametern: Prozent-Kodierung verwenden. Wenn ein Wert beide durchläuft (z. B. eine Suchanfrage in einem Link-href-Attribut), wenden Sie zuerst URL-Kodierung auf den Abfragewert an und kodieren dann den gesamten href, bevor Sie ihn in das Markup einfügen.

Praxisbeispiele, in denen Kodierung wichtig ist

HTML-Kodierung ist keine akademische Übung. Jede Anwendung, die benutzergenerierte Inhalte anzeigt oder dynamische Daten rendert, begegnet diesen Situationen täglich:

  • Code-Schnipsel in Blogs oder Dokumentation: Ein Tutorial, das einen Python-Vergleich wie if x < 10 and y > 5: zeigt, wird das umgebende HTML brechen, wenn die spitzen Klammern nicht escaped sind. Das < startet ein Tag, der Browser geht in den Fehlerwiederherstellungsmodus, und die Ausgabe ist verstümmelt.
  • Benutzerkommentare und Forum-Beiträge: Jede Plattform, die Freitexteingaben akzeptiert — Kommentare, Bewertungen, Chat-Nachrichten — muss die Ausgabe kodieren. Ein Kommentar, der einfach a < b schreibt, ist nicht böswillig, aber das unescaped < wird trotzdem die Darstellung brechen.
  • JSON innerhalb von Script-Tags: Serverseitig gerendertes JSON, das in einem <script>-Block eingebettet ist, erfordert sorgfältige Behandlung. Strings, die </script> enthalten, schließen das Script-Tag vorzeitig. Das sichere Muster: < und > in JSON-String-Werten kodieren oder einen type="application/json" Datenblock ohne JavaScript-Parsing verwenden.
  • Mailto-Links mit Sonderzeichen: Ein Mailto-Links Betreffzeile mit & muss im HTML-href als &amp;amp; kodiert werden, während der eigentliche Betreffparameter Prozent-Kodierung für Leerzeichen und Nicht-ASCII-Zeichen verwendet.
  • Doppelte Ampersands in Attributen: href="search?q=a&&b" direkt in HTML zu schreiben ist problematisch. Der Browser sieht den zweiten & als Beginn einer Entity-Referenz. Beide Ampersands müssen kodiert werden: href="search?q=a&amp;amp;b".
  • Kleiner-als in Data-Attributen: Ein Data-Attribut wie data-config="{min: 0, max: 10}" ist unproblematisch, aber data-template="<div>content</div>" muss vollständig kodiert werden, denn < und > werden vom HTML-Parser interpretiert, noch bevor JavaScript das Attribut sieht.

Über- und Unter-Escaping: die beiden Fehlermodi

Die Kodierung in beide Richtungen falsch zu machen, erzeugt sichtbare Fehler. Unter-Escaping bedeutet, dass Sie nicht genug Zeichen kodiert haben. Die Folgen reichen von kaputten Layouts (ein umherirrendes < stiehlt einen Teil der Seite) bis hin zu vollständigen XSS-Schwachstellen. Unter-Escaping ist der gefährlichere Fehler, weil er die Sicherheit betrifft, nicht nur die Darstellung.

Über-Escaping bedeutet, dass Sie Zeichen kodiert haben, die nicht kodiert werden mussten, oder dieselben Zeichen mehrfach kodiert haben. Ein Benutzername wie Tom & Jerry erscheint als Tom &amp;amp; Jerry, wenn der Ampersand doppelt kodiert wird. Das passiert, wenn eine Pipeline dieselbe Zeichenkette an zwei verschiedenen Stellen kodiert — die Anwendung kodiert einmal, dann kodiert ein Template-Engine- oder Content-Sicherheitslayer erneut. Das Ergebnis ist unlesbarer Text und fehlerhafte Darstellung.

Der Weg, beide Probleme zu vermeiden, ist eine klare Regel: Einmal kodieren, am Ausgabepunkt, und niemals davor. Speichern Sie den Rohwert in Ihrer Datenbank. Wenden Sie HTML-Kodierung genau dann an, wenn Sie ihn in HTML-Markup einfügen. Kodieren Sie nicht beim Speichern, nicht beim Weitergeben zwischen Backend-Funktionen und nicht beim Speichern in einer Template-Variable. Kodierung an einem einzigen Punkt verhindert doppelte Kodierung; konsequente Kodierung an der Ausgabe verhindert Unter-Kodierung.

// FALSCH: Kodierung beim Speichern
db.save({ comment: encodeHtml(userInput) }); // Doppel-Kodierungs-Risiko

// RICHTIG: Kodierung am Ausgabepunkt
const comment = db.getComment(id); // Rohtext
element.innerHTML = encodeHtml(comment); // Hier einmal kodieren

Welche Zeichen in welchem Kontext tatsächlich kodiert werden müssen

Nicht jedes Zeichen muss in jedem Kontext kodiert werden. Das Minimum hängt davon ab, wo der Text erscheint:

  • In Elementinhalten (zwischen öffnenden und schließenden Tags): & und < kodieren. > ist technisch optional, wird aber dringend empfohlen — Browser tolerieren unescaped > in Inhalten, aber es kann in Grenzfällen mit CDATA-Abschnitten und benachbarten Textknoten zu Parsing-Problemen führen.
  • In doppelt angeführten Attributen: &, < und " kodieren. < kommt selten in Attributwerten vor, muss aber bei Vorhandensein escaped werden.
  • In einfach angeführten Attributen: Statt doppelter Anführungszeichen &, < und ' kodieren.
  • In JavaScript-Strings, die in HTML eingefügt werden: Alle fünf Pflicht-Entities. Wenn eine JS-Variable in innerHTML, Template Literal oder document.write fließt, muss jedes Sonderzeichen kodiert werden.
  • Innerhalb von CDATA-Abschnitten (XML/XHTML): Innerhalb von <![CDATA[...]]>-Blöcken können Sie die Entity-Kodierung vollständig umgehen — außer der Folge ]]>, die niemals unescaped erscheinen darf. CDATA ist relevant für SVG und MathML in HTML5-Dokumenten.

Browser-Quirks und Grenzfälle

Moderne Browser sind nachsichtig bei vielen HTML-Fehlern, aber diese Nachsichtigkeit erzeugt ein falsches Sicherheitsgefühl. Spezifische Grenzfälle, auf die Sie achten sollten:

  • Unzitierte Attribute mit Sonderzeichen: class=foo&bar ohne Anführungszeichen zu schreiben, wird brechen, weil der Browser & als Entity-Anfang sieht. Zitieren Sie Attributwerte immer und kodieren Sie den Inhalt dieser Zitate.
  • Die Elemente ohne Schließ-Tag: Elemente wie <script>, <style> und <textarea> verwenden einen speziellen Parsing-Modus. Innerhalb dieser Elemente findet in HTML5 fast keine Entity-Decodierung statt — Sie müssen < und & kodieren, um vorzeitiges Tag-Schließen zu verhindern, aber benannte Entities wie &amp;nbsp; werden als wörtlicher Text behandelt.
  • Ungültige Entity-Referenzen: Der Browser lässt Semikolons bei unbekannten Entities stillschweigend weg. &foo; gibt in den meisten Browsern den wörtlichen Text &foo; aus, aber &foo (ohne Semikolon) kann das nächste Zeichen verschlingen — ein Bug, der extrem schwer zu verfolgen ist.
  • Numerische Zeichenreferenzen und Steuerzeichen: Die Verwendung numerischer Entities für Steuerzeichen wie &#x0; (Null) ist in HTML5 verboten. Der Parser ersetzt sie stillschweigend durch U+FFFD (Ersetzungszeichen) oder lässt sie vollständig weg.

Die Semikolon-Falle

Eine Entity-Referenz ohne abschließendes Semikolon ist mehrdeutig. &amp;lt ohne Semikolon wird als Entity &amp;lt gefolgt vom nächsten Zeichen geparst. Ein Benutzer, der &amp;cat tippt, erzeugt versehentlich die Entity &amp;ca gefolgt von einem losen t. Stellen Sie sicher, dass Ihre Kodierungsfunktion für jede erzeugte Entity das Semikolon enthält.

Kodierungsstrategien in der Praxis

Die meisten Web-Frameworks enthalten eingebaute Kodierungsfunktionen. Reacts JSX kodiert automatisch Ausdruckswerte innerhalb von Elementinhalten (aber dangerouslySetInnerHTML umgeht dies vollständig — verwenden Sie es nur mit vertrauenswürdigen, vorsanitisierten Inhalten). Serverseitige Template-Engines wie Jinja2, Handlebars und Blade bieten Escape-Filter. Der Kernpunkt: Wissen Sie, wo Ihr Framework automatisch kodiert und wo nicht.

Für die fünf Pflicht-Entities ist eine minimale Kodierungsfunktion unkompliziert. Die Funktion ersetzt jedes Sonderzeichen durch seine benannte Entity, Ampersands zuerst:

function encodeHtml(str) {
  return str
    .replace(/&/g, "&amp;")   // muss zuerst kommen
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

// Verwendung:
const safe = encodeHtml('He said "a < b & c > d"');
// → He said &quot;a &lt; b &amp; c &gt; d&quot;

Bevorzugen Sie die Standardbibliothek

Die manuelle Funktion oben dient zum Verstehen, nicht für den Produktivbetrieb. Verwenden Sie in Node.js die eingebaute Text-Encoding-API oder eine gut getestete Bibliothek. Im Browser erledigt die textContent-Zuweisung die meisten Fälle automatisch. Die DOMParser-API ist der sicherste Weg für Benutzer-HTML, wenn Sie einige Tags erlauben, aber andere blockieren wollen — sie parsed ohne Ausführung von Skripten.

HTML-Kodierungswerkzeuge auf Convrs

Wenn Sie HTML-Entities schnell kodieren oder decodieren müssen — ob Sie ein Darstellungsproblem debuggen, Inhalte für ein CMS aufbereiten oder testen, wie kodierter Text aussieht — das HTML Encode/Decode-Werkzeug auf Convrs handhabt beide Richtungen im Browser. Fügen Sie Ihren Text ein, wählen Kodieren oder Decodieren und erhalten sofort das Ergebnis. Kein Datei-Upload, keine Serverrundreise, keine Daten, die Ihr Gerät verlassen. Es umfasst alle fünf Pflicht-Entities plus numerische Referenzen, sodass Sie überprüfen können, dass Ihre Ausgabekodierung mit dem übereinstimmt, was der Browser erwartet.

Buy me a coffee