HTML-Entity-Encoder und -Decoder

Maskieren Sie Text, damit er in HTML sicher ist, oder decodieren Sie benannte und numerische Entities zurück in die Zeichen, für die sie stehen.

Text
Codiert

Die Ausgabe erscheint hier

Was dieses Werkzeug tut

HTML kann ein Kleiner-als-Zeichen, das Sie als Text gemeint haben, nicht von einem unterscheiden, das ein Tag eröffnet. Die Sprache löst das mit Entities: kurzen Escape-Sequenzen, die für ein Zeichen stehen, ohne als Markup gelesen zu werden. Dieses Werkzeug wandelt in beide Richtungen um — Klartext in seine maskierte Form und maskiertes HTML zurück in die Zeichen, für die es steht.

Beide Richtungen laufen vollständig in Ihrem Browser. Das zählt hier mehr als bei den meisten Werkzeugen, denn der Text, den man in einen Entity-Decoder einfügt, ist meist eine ausgelesene Seite, eine Kunden-E-Mail oder eine Datenbankspalte, die jemand zu verstehen versucht.

Die fünf Zeichen, auf die es ankommt

Es gibt Tausende von Entities, aber nur eine Handvoll ist nötig, um Text sicher zu machen. Das Codieren ersetzt hier genau fünf Zeichen und lässt alles andere lesbar:

  • Das Und-Zeichen wird zu & — dieses zuerst, sonst würde jede weitere Ersetzung selbst wieder als Entity gelesen.
  • Das Kleiner-als-Zeichen wird zu < — das Zeichen, das ein Tag eröffnen kann.
  • Das Größer-als-Zeichen wird zu > — für sich genommen harmloser, aus Symmetrie maskiert und um nachlässige Parser zu überstehen.
  • Das doppelte Anführungszeichen wird zu " — nötig in Attributwerten, die mit doppelten Anführungszeichen geschrieben sind.
  • Der Apostroph wird zu ' — nötig in Attributwerten, die mit einfachen Anführungszeichen geschrieben sind.

Der letzte wird absichtlich numerisch geschrieben. Es gibt einen Namen dafür, ', aber er stammt aus XML und HTML 4 hat ihn nie definiert; alte Parser reichen ihn als wörtlichen Text durch, und die Maskierung schlägt still fehl. Die numerische Form hat immer überall funktioniert, weshalb ernstzunehmende Maskierungsbibliotheken sie ausgeben.

Benannt, dezimal und hexadezimal

Jedes Zeichen lässt sich auf drei Arten schreiben, und das Decodieren akzeptiert alle. Der Geviertstrich ist ein gutes Beispiel:

named     —      die lesbare Form, aber nur für Zeichen mit Namen
decimal   —      der Codepunkt zur Basis 10
hex       —     derselbe Codepunkt zur Basis 16, die Notation der Unicode-Tabellen

Beim Decodieren kennt dieses Werkzeug die 255 Namen des HTML-4-Satzes — die lateinischen Buchstaben, das griechische Alphabet, die Pfeile, die mathematischen Symbole und die typografischen Satzzeichen, die zusammen praktisch alles abdecken, was in echtem Text vorkommt. HTML 5 definiert rund 2200 weitere, fast durchweg entlegene mathematische Symbole; diese Tabelle mitzuschleppen würde jedem Seitenaufruf Dutzende Kilobyte hinzufügen, im Tausch gegen Namen, die die meisten Dokumente nie verwenden. Ein Name außerhalb des Satzes wird gemeldet statt verworfen — der Preis ist eine Warnung, niemals eine falsche Antwort.

Das Codieren geht den umgekehrten Weg und bleibt bewusst schlicht: Namen für die fünf Zeichen oben, Dezimalzahlen für alles andere. Nichts wird zu … oder ’, wenn das Zeichen selbst in einem UTF-8-Dokument völlig gültig ist.

Maskieren ist nicht eine Regel, sondern vier

Es liegt nahe, Entity-Maskierung für die Antwort auf Injection zu halten, und innerhalb von Elementinhalten ist sie das auch wirklich. Doch HTML besteht eigentlich aus vier ineinander verschachtelten Sprachen, jede mit eigenen Regeln. Entity-Maskierung ist an den ersten beiden dieser Stellen richtig und an den übrigen nutzlos:

  • Elementinhalt — Text zwischen Tags. Entity-Maskierung ist genau das Richtige.
  • Attributwerte in Anführungszeichen. Entity-Maskierung ist richtig, sofern das Attribut wirklich in Anführungszeichen steht; ohne sie beendet ein Leerzeichen im Wert das Attribut, und die Maskierung bringt Ihnen nichts.
  • Innerhalb eines script-Blocks. Dort gelten die Regeln zur Maskierung von JavaScript-Zeichenketten, nicht Entities — der Browser decodiert dort keine Entities, sodass ein maskiertes Anführungszeichen als die wörtlichen Zeichen " ankommt.
  • URL-Attribute wie href und src. Entity-Maskierung stoppt keine javascript:-URL, denn die Gefahr steckt im Schema und nicht in einem Zeichen, das eine Entity ersetzen würde.

Die praktische Regel: Maskieren Sie an der Stelle, an der Text in ein Dokument eingefügt wird, nach den Regeln genau des Kontexts, in dem er landet — nicht einmal, früh, in der Hoffnung, dass es später überall hält.

Warum manchmal & erscheint

Das ist mit Abstand der häufigste Grund, zu einem Entity-Decoder zu greifen. Der Text wurde maskiert und dann noch einmal maskiert — meist einmal vom Anwendungscode und einmal von einer Template-Engine oder einem Framework, das seine Ausgabe ohnehin schon maskiert:

original     Fish & Chips
escaped      Fish & Chips
escaped x2   Fish & Chips

Die zweite Zeile erscheint auf der Seite wie das Original. Die dritte erscheint wie die zweite — der Besucher sieht also die Entity statt des Zeichens. Das Decodieren entfernt genau eine Schicht, ein einziger Durchlauf sagt Ihnen also, welcher Fall vorliegt. Enthält die Ausgabe noch Entities, waren es mehrere Schichten. Die Lösung besteht fast nie darin, Entities bei der Anzeige zu entfernen — es gilt, den doppelten Maskierungsschritt zu finden und zu entfernen, denn derselbe Fehler beschädigt still auch die gespeicherten Daten.

Nicht-ASCII und die Option dafür

Eine als UTF-8 ausgelieferte Seite braucht keine Entities für Buchstaben mit Akzenten, Hebräisch, Arabisch, Chinesisch oder Emoji. Diese Zeichen sind so, wie sie sind, gültig, und sie lesbar zu lassen hält den Quelltext lesbar und die Datei kleiner. Das ist hier die Voreinstellung.

Die Option gibt es, weil manche Ziele weiterhin auf reinem ASCII bestehen: ältere E-Mail-Vorlagen, Systeme, die Bytes oberhalb von 127 verstümmeln, oder eine Datenbankspalte, deren Codierung niemand ändern will. Eingeschaltet schreibt sie jedes solche Zeichen als Dezimalreferenz — aus é wird é, aus א wird א.

Emoji verdienen eine Anmerkung. Ein Emoji wird als eine Referenz für seinen gesamten Codepunkt geschrieben, etwa 🙂, niemals als zwei Hälften. Wer das JSON-Maskierungswerkzeug benutzt hat, kennt dort das Gegenteil: das Format verlangt dort ausdrücklich, ein Emoji in zwei \u-Escapes zu zerlegen. Dasselbe Zeichen, zwei richtige Antworten, weil die beiden Formate Maskierung über unterschiedliche Einheiten definieren.

Wenn eine Referenz nicht decodiert werden kann

Das Decodieren schlägt hier nie fehl und löscht nie etwas. Eine Referenz, die sich nicht auflösen lässt, bleibt genau so stehen, wie sie gefunden wurde, und wird über der Ausgabe mit Zeile und Spalte aufgeführt. Drei Dinge werden gemeldet:

  • Ein unbekannter Name wie &nbps; — fast immer ein Tippfehler für  . Das ist die Warnung, auf die es sich zu reagieren lohnt: In Ihrem Inhalt fehlt ein Leerzeichen, und niemand hat es bemerkt.
  • Eine Zahl, die kein Zeichen ist, etwa � oder ein Wert oberhalb des Unicode-Bereichs. Es gibt nichts, wozu sie decodiert werden könnte, also bleibt sie Text.
  • Eine Zahl zwischen 128 und 159. Siehe unten.

Ein einzelnes Und-Zeichen wird gar nicht gemeldet. In HTML sind R&D und Q&A gewöhnlicher Text, und jedes verirrte Und-Zeichen als Fehler zu behandeln würde die wichtigen Warnungen unter dem Rauschen aus Fließtext begraben.

Der Bereich von 128 bis 159 ist eine echte Kuriosität. Diese Zahlen bezeichnen unsichtbare Steuerzeichen, die niemand je schreiben will; sie tauchen auf, weil ein Dokument in Windows-1252 verfasst wurde, wo dieselben Zahlen typografische Anführungszeichen, Striche und das Eurozeichen sind. Browser lesen sie genau deshalb stillschweigend als Windows-1252, und dieses Werkzeug hält es ebenso, damit das Ergebnis dem entspricht, was eine Seite tatsächlich anzeigen würde — sagt es aber in den Warnungen, denn eine stille Ersetzung, um die Sie nicht gebeten haben, ist schlimmer als eine laute.

Häufig gestellte Fragen

Genügt das Maskieren dieser fünf Zeichen gegen XSS?
In Elementinhalten und in Attributwerten in Anführungszeichen ja — genau dafür ist es gedacht. In einem script- oder style-Block, in einem Attribut ohne Anführungszeichen oder in einem URL-Attribut wie href genügt es nicht; jeder dieser Orte braucht eigene Regeln. Maskierung ist eine Eigenschaft des Ortes, an dem Text landet, nicht des Textes.
Warum wird der Apostroph als ' und nicht als ' codiert?
Weil ' eine XML-Entity ist, die HTML 4 nie definiert hat. Parser vor HTML 5 belassen sie als wörtlichen Text, sodass ein vermeintlich maskierter Apostroph gar nicht maskiert ist. Die numerische Form funktioniert in jedem Parser und ist deshalb die sichere Voreinstellung.
Meine Seite zeigt & statt eines Und-Zeichens. Was ist passiert?
Der Text wurde zweimal maskiert. Decodieren Sie ihn hier: ein Durchlauf macht aus & ein & und bestätigt damit die überflüssige Schicht. Reparieren Sie die Verarbeitungskette statt des Textes — meist maskiert Ihr Code eine Ausgabe, die die Template-Engine schon maskiert hat.
Muss ich Hebräisch, Arabisch oder Emoji codieren?
Nicht auf einer modernen, als UTF-8 ausgelieferten Seite; sie sind so gültig, wie sie sind. Schalten Sie die Nicht-ASCII-Option nur ein, wenn etwas weiter hinten auf reinem ASCII besteht, etwa eine alte E-Mail-Vorlage. Jedes Emoji wird dann als eine einzige numerische Referenz geschrieben.
Warum warnt das Werkzeug bei “, wenn es doch richtig decodiert hat?
Weil 147 ein unsichtbares Steuerzeichen bezeichnet und nicht das Anführungszeichen, das Sie sehen. Dokumente, die es verwenden, wurden in Windows-1252 geschrieben, wo 147 ein typografisches Anführungszeichen ist — Browser lesen es so, und dieses Werkzeug ebenfalls. Die Warnung sagt Ihnen, dass eine Ersetzung stattgefunden hat, damit Sie die Quellcodierung korrigieren können, falls sie in Ihrer Hand liegt.
Wird mein Text irgendwohin gesendet?
Nein. Codieren und Decodieren laufen vollständig in Ihrem Browser; nichts, was Sie einfügen, verlässt Ihr Gerät.