HTML-Sonderzeichen kodieren

Füge Text ein und bekomme ihn zurück, wobei die Zeichen, die verändern, wie ein Dokument geparst wird, durch ihre Ersatzschreibweisen ersetzt sind. Das ist der Handgriff, der aus einem Text, den jemand anderes geschrieben hat, Inhalt statt Struktur macht — und die Stelle, an der eine ausgelassene Ersetzung aus einem Kommentarfeld ein Sicherheitsproblem macht. Die Rechnung passiert in dieser Seite, hochgeladen wird nichts.

Das erste ist, was eine Template-Engine tut. Das zweite ist für eine Verarbeitungskette, die nicht sauber UTF-8 spricht.

Ergebnis

Die Antwort erscheint hier, während du tippst.

  • Wo es läuft

    Es wird nichts hochgeladen, weil es keine Datei gibt — gerechnet wird in dieser Seite.

  • Keine Warteschlange, kein Konto

    Es antwortet so schnell, wie dein Rechner es hergibt, und fragt nie, wer du bist.

  • So oft du willst

    Nichts wird gezählt und nichts begrenzt — noch einmal zu antworten kostet uns nichts.

So funktioniert es

  1. Füge den Text oder das Markup-Schnipsel in das Feld ein.
  2. Wähle, wie weit maskiert werden soll — nur die Zeichen, die den Parse verändern, oder zusätzlich alles über ASCII.
  3. Kopiere das Ergebnis. Es hat den Rechner nicht verlassen.

Die fünf Zeichen, um die es tatsächlich geht

Ein HTML-Parser interessiert sich für sehr wenige Zeichen. Ein spitzes Klammernpaar beginnt und beendet ein Tag, ein kaufmännisches Und beginnt eine Zeichenreferenz, und in Attributwerten beenden das doppelte und das einfache Anführungszeichen den Wert. Das sind fünf Zeichen, und alles andere ist für den Parser gewöhnlicher Inhalt.

Deshalb maskiert die sparsame Einstellung genau diese fünf und sonst nichts. Ein Kodierer, der aus jedem Umlaut eine Entität macht, ist nicht sicherer — er erzeugt nur eine Ausgabe, die vier- bis fünfmal so lang und für Menschen unlesbar ist, während er gegen genau dieselben Angriffe schützt wie die kurze Variante.

Warum das kaufmännische Und zuerst ersetzt werden muss

Wer selbst ersetzt, muss mit dem `&` anfangen. Kommt es später an die Reihe, hat der Kodierer bereits eigene Entitäten erzeugt, deren `&` er dann noch einmal maskiert — aus einem `<` wird `&lt;` und daraus `&amp;lt;`, und die Seite zeigt den Text `&lt;` an statt eines spitzen Klammernpaars.

Das ist der mit Abstand häufigste Fehler in handgeschriebenen Escape-Funktionen und einer der wenigen, die man an der Ausgabe sofort erkennt: Wenn im Ergebnis `&amp;` vor anderen Entitätsnamen steht, ist die Reihenfolge falsch. Dieses Werkzeug arbeitet die Ersetzung in einem Durchgang ab, das Problem kann hier also nicht entstehen.

Textinhalt und Attributwert sind nicht dasselbe

Zwischen zwei Tags reichen drei Ersetzungen: kleiner-als, größer-als und das kaufmännische Und. In einem Attributwert kommen die Anführungszeichen dazu, denn dort beendet ein doppeltes Anführungszeichen den Wert, und was danach steht, liest der Parser als weiteres Attribut — der klassische Einstieg, um ein `onmouseover` in ein fremdes Tag zu schmuggeln.

Besonders zäh wird es bei unquotierten Attributwerten, die HTML erlaubt: Dort beendet schon ein Leerzeichen den Wert, und Maskieren allein reicht nicht mehr. Die praktische Regel lautet, Attributwerte immer in Anführungszeichen zu setzen und alle fünf Zeichen zu maskieren; die Einstellung hier tut genau das.

Wann „alles über ASCII“ die richtige Wahl ist

Die zweite Einstellung schreibt zusätzlich jedes Zeichen jenseits von ASCII als numerische Referenz, aus `ü` wird also `&#252;`. Für die Sicherheit bringt das nichts, für die Lesbarkeit ist es ein Verlust — es hilft ausschließlich dort, wo irgendein Glied der Kette kein UTF-8 verträgt.

Solche Glieder gibt es noch: alte E-Mail-Vorlagen, Exporte in Systeme mit fest verdrahteter Latin-1-Annahme, gelegentlich ein Datenbankfeld mit falscher Kollation. Wenn Umlaute unterwegs zu Fragezeichen oder zu einem Ä-Doppelzeichen werden, ist die numerische Schreibweise ein belastbarer Umweg. Wo die Kette sauber UTF-8 spricht, ist sie überflüssiges Gewicht.

Kodieren ist Ausgabe-Hygiene, kein Filter

Maskieren schützt an genau einer Stelle: beim Einsetzen in HTML. Es ist keine Eingabeprüfung und ersetzt keine. Wer beim Speichern maskiert statt beim Ausgeben, hat später Daten in der Datenbank, die in einer JSON-Antwort, einer CSV-Ausgabe oder einer E-Mail als `&amp;` auftauchen — an Orten also, wo HTML nie im Spiel war.

Die Reihenfolge, die funktioniert, ist umgekehrt: unverändert speichern und beim Rendern für das jeweilige Ziel maskieren. Dieselben Daten gehen dann korrekt in HTML, in JSON und in eine Nur-Text-Mail, jeweils mit den Regeln, die dort gelten — und niemand muss raten, in welchem Zustand ein Feld gerade ist.

Drei Schreibweisen für dasselbe Zeichen

HTML kennt benannte Referenzen wie `&amp;`, dezimale wie `&#38;` und hexadezimale wie `&#x26;`. Alle drei bezeichnen dasselbe Zeichen, und ein Parser akzeptiert alle drei. Dieses Werkzeug erzeugt die benannten für die fünf entscheidenden Zeichen, weil die im Quelltext lesbar sind, und numerische für alles darüber.

Beim Lesen fremder Ausgaben begegnen einem alle drei gemischt, oft in derselben Datei, weil verschiedene Bausteine sie erzeugt haben. Das ist kein Fehler und kein Hinweis auf ein Problem — es sagt nur, dass an dem Dokument mehr als ein Werkzeug beteiligt war.

Warum hier `&#39;` und nicht `&apos;` steht

Für das einfache Anführungszeichen gibt es einen benannten Namen, `&apos;`, aber er stammt aus XML und wurde erst mit HTML5 offiziell Teil von HTML. In HTML 4 war er nicht definiert, und sehr alte Parser geben ihn deshalb wörtlich aus statt ihn aufzulösen.

Die numerische Schreibweise `&#39;` hat dieses Problem nie gehabt und funktioniert überall, weshalb sie bis heute in fast jeder Escape-Bibliothek die Vorgabe ist. Das ist einer der Fälle, in denen eine zwanzig Jahre alte Inkompatibilität die Konvention geprägt hat, obwohl der Anlass praktisch verschwunden ist.

Doppelt maskiert, und woran man es erkennt

Fast jede Template-Engine maskiert von sich aus — Twig, Blade, Jinja, JSX und Razor tun es standardmäßig. Wer denselben Wert vorher schon einmal durch eine Escape-Funktion geschickt hat, sieht in der fertigen Seite `&lt;b&gt;` als sichtbaren Text stehen statt eines fetten Worts.

Das Erkennungszeichen ist ein `&amp;` unmittelbar vor einem anderen Entitätsnamen. Die Lösung ist immer, eine der beiden Stellen zu entfernen, und nie, am Ende einmal zusätzlich zu dekodieren — denn das entschärft dann auch die Maskierung, die dort hingehört, und macht aus einem Anzeigefehler ein Sicherheitsproblem.

HTML maskieren rettet kein JavaScript

Ein Wert, der in einen `<script>`-Block geschrieben wird, steht nicht mehr in HTML-Kontext, sondern in JavaScript-Kontext, und dort gelten andere Regeln. Ein maskiertes Anführungszeichen hilft dort nicht; umgekehrt kann eine Zeichenkette, die in HTML völlig harmlos ist, dort ein Skript beenden.

Dasselbe gilt für einen Wert in einem `href`, einem `style` oder einem `data-`-Attribut, das später ausgewertet wird. Jeder dieser Orte hat seine eigene Maskierung, und die richtige Frage ist nie „ist das maskiert“, sondern „ist es für den Ort maskiert, an dem es landet“. Dieses Werkzeug beantwortet den HTML-Fall.

Was das für die DSGVO bedeutet

Was in dieses Feld eingefügt wird, ist regelmäßig fremder Text — ein Nutzerkommentar, eine Support-Nachricht, eine Produktbeschreibung mit Namen darin. Weil die Ersetzung in der Seite passiert, findet keine Übermittlung an uns statt und damit keine Auftragsverarbeitung für diesen Inhalt.

Nachprüfbar ist das im Netzwerk-Panel des Browsers: Während der Benutzung geht keine Anfrage hinaus, die deine Eingabe trägt. Die Seite selbst kommt von einem CDN und die Website trägt Werbung, es gibt also Anfragen — sie kennen den Umstand deines Besuchs, aber nicht den Inhalt des Textfelds.

Wann man gar nicht maskieren sollte

Wer bewusst Markup ausliefert — eine Newsletter-Vorlage, ein Redaktionssystem, in dem Autoren HTML schreiben dürfen — will genau nicht, dass die Tags zu Text werden. Dort ist Maskieren die falsche Antwort, und die richtige ist eine Bereinigung, die eine Liste erlaubter Elemente und Attribute durchsetzt.

Die Grenze verläuft an der Frage, ob das Markup Teil des Inhalts sein soll. Soll es das nicht, maskiert man vollständig; soll es das, braucht man einen Sanitizer und keine Escape-Funktion. Beides zu vermischen — ein bisschen maskieren, ein paar Tags durchlassen — ist der Weg, auf dem die meisten Lücken entstehen.

HTML-Sonderzeichen kodieren: häufige Fragen

Wird mein Text an einen Server geschickt?

Nein. Die Ersetzung passiert in dieser Seite, in deinem Browser. Öffne das Netzwerk-Panel während du tippst, dann siehst du, dass keine Anfrage hinausgeht, die deine Eingabe trägt.

Warum werden meine Umlaute nicht maskiert?

Weil sie für einen HTML-Parser gewöhnliche Zeichen sind und in einem UTF-8-Dokument nichts kaputtmachen. Wenn deine Verarbeitungskette kein UTF-8 verträgt, wähle die zweite Einstellung — dann werden sie als numerische Referenzen geschrieben.

Schützt das gegen XSS?

An der Stelle, an der HTML zusammengesetzt wird, ja — das ist genau der Zweck. Es ersetzt aber keine Eingabeprüfung und gilt nicht für Werte, die in JavaScript, in einem href oder in einem CSS-Kontext landen. Dort gelten jeweils eigene Regeln.

Warum sehe ich im Ergebnis `&amp;lt;` statt `&lt;`?

Dann war der Text bereits maskiert und wurde ein zweites Mal maskiert. Das ist kein Fehler des Werkzeugs, sondern der Hinweis, dass an einer früheren Stelle in deiner Kette schon eine Escape-Funktion gelaufen ist — meist die deiner Template-Engine.

Gibt es eine Längenbegrenzung?

Nur das, was dein eigener Browser mitmacht. Es rechnet kein Server mit, der etwas zählen oder deckeln könnte; sehr große Eingaben lassen den Tab kurz nachdenken, und das ist die einzige Grenze.

Weitere Werkzeuge