HTML-Escaping ersetzt die Handvoll Zeichen, die sonst als Markup gelesen würden, durch Entitätsreferenzen, damit Text Text bleibt. Eine kleine Operation mit übergroßem Ruf, denn sie falsch zu machen ist der Weg, auf dem Cross-Site-Scripting entsteht — und weil "maskiere die Eingabe" ein Rat ist, der subtil und gefährlich unvollständig ist.
Fünf Zeichen, und die Reihenfolge, in der du sie ersetzt
Die Menge ist klein: & wird zu &, < zu <, > zu >, " zu " und ' zu '. Alles andere kann bleiben, wie es ist.
In dieser Liste versteckt sich ein klassischer Fehler. Ersetzt du die Zeichen nacheinander und behandelst & zuletzt, zerstörst du deine eigene Arbeit: < wird zu <, dann wird das gerade eingeführte & zu &, und heraus kommt &lt; — ein wörtliches, sichtbares "<" auf der Seite statt eines Kleiner-als-Zeichens. Das kaufmännische Und muss immer zuerst maskiert werden, denn jede andere Ersetzung führt eines ein.
Dieses Tool umgeht die Falle vollständig, indem es alle fünf Zeichen in einem Durchgang matcht und jedes genau einmal ersetzt — die Reihenfolge stellt sich also gar nicht. Das ist die robustere Form für solchen Code und nachahmenswert: Eine einzelne Regex mit Nachschlagetabelle kann nicht doppelt kodieren, während eine Kette von replace-Aufrufen davon abhängt, dass ein Entwickler sich an eine Invariante erinnert.
Warum das Apostroph numerisch ist
Schau genau hin: Das einfache Anführungszeichen wird zu ' und nicht zu ', was neben " inkonsequent aussieht. Das ist Absicht. ' wurde in XML definiert und erreichte HTML erst in Version 5 — es ist nicht Teil von HTML 4, und sehr alte Parser stellen es wörtlich als die vier Zeichen ' dar statt als Apostroph.
Die numerische Referenz ' hat immer und überall funktioniert. Eine Kleinigkeit, aber die Art Kleinigkeit, die als verstümmelter Text in einem E-Mail-Client auftaucht, der HTML von 2003 rendert — und die numerische Form kostet nichts.
Maskieren ist kontextabhängig — das ist der entscheidende Teil
Hier das Missverständnis, das es zu zerlegen lohnt: "maskiertes HTML" im Abstrakten gibt es nicht. Es gibt nur Text, der für einen bestimmten Ort korrekt maskiert ist, und dieselbe Maskierung, die an einer Position vollkommen sicher ist, ist an einer anderen nutzlos.
Zwischen Tags reicht es, diese fünf Zeichen zu maskieren. In einem quotierten Attribut reicht es ebenfalls — vorausgesetzt, das Attribut hat tatsächlich Anführungszeichen. Schreib <img src=WERT> ohne sie, und ein maskierter Wert mit einem Leerzeichen bricht trotzdem aus: x onerror=alert(1) enthält kein Zeichen, das HTML-Escaping anfasst, und wird zu einem Attribut. Die Anführungszeichen leisten die Sicherheitsarbeit, nicht die Maskierung.
Anderswo wird es schlimmer. In einem <script>-Block gelten HTML-Regeln nicht — du brauchst JavaScript-String-Escaping, und die Folge </script> beendet den Block, wo immer sie in einem Stringliteral steht, ungeachtet der Umgebung. In einem URL-Attribut tut HTML-Escaping nichts gegen javascript: als Schema. In einem style-Attribut hat CSS seine eigene Escape-Syntax. Fünf verschiedene Kontexte, fünf verschiedene Regeln — und eine Template-Engine, die nur einen davon kennt, erzeugt bereitwillig ein Loch.
Dekodieren ist das Gegenteil von Sicherheit
Kodieren macht Text inert. Dekodieren nimmt inerten Text und macht ihn wieder lebendig. Etwas zu dekodieren und dann per innerHTML in eine Seite zu setzen macht also genau den Schutz zunichte, den die Kodierung bot — und das ist ein reales Muster in realem Code, meist geschrieben von jemandem, der "dekodieren" für "säubern" hielt.
Mit diesem Tool zu dekodieren ist sicher, weil es mit dem Ergebnis nichts tut, außer es dir in einer Textarea zu zeigen, die nichts ausführen kann. Das Risiko lebt in dem, was du danach tust. Dekodierst du Entitäten, um das Ergebnis in eine Seite einzufügen, nimm textContent statt innerHTML; und musst du wirklich benutzerdefiniertes HTML rendern, brauchst du einen Sanitizer wie DOMPurify — eine Positivliste sicherer Tags und Attribute — keinen Entitäten-Dekodierer.
Häufige Fragen
Wird mein Text an einen Server geschickt?
Nein. Dieses Tool ist als "client" gekennzeichnet: Die Ersetzung läuft in deinem Browser-Tab, es wird nichts übertragen und nichts gespeichert.
Bekomme ich durch Kodieren und dann Dekodieren exakt das zurück, womit ich begonnen habe?
Ja. Die fünf Ersetzungen sind umkehrbar und eindeutig, ein Hin und Zurück durch dieses Tool liefert den Originaltext also Zeichen für Zeichen zurück, samt kaufmännischem Und und Anführungszeichen. Das gilt nicht für jedes Escaping-Verfahren, aber für dieses.
Reicht Maskieren, um XSS zu verhindern?
Nur wenn es zum Kontext passt, wie oben beschrieben. Die ehrliche Kurzfassung: Manuelles Maskieren ist der fragile Weg. Moderne Frameworks — React, Vue, Angular und jede aktuelle serverseitige Template-Engine — maskieren standardmäßig und wissen, in welchem Kontext sie sind. Das ist die eigentliche Verteidigung. Ein Werkzeug wie dieses dient dazu, einen Wert zu untersuchen und zu verstehen, nicht dazu, die Sicherheitsschicht einer Anwendung zu sein.
Und was ist mit und all den anderen benannten Entitäten?
Die HTML-Spezifikation definiert über zweitausend davon, und fast keine wird noch gebraucht. In einem UTF-8-Dokument kannst du é, ©, — und 😀 direkt schreiben, und das ist lesbarer, als é, © und — es je waren. Die Ausnahmen, die man behalten sollte, sind die fünf strukturellen, plus , wo du gezielt ein Leerzeichen willst, das nicht umbricht. Benannte Entitäten anderswo sind eine Gewohnheit aus einer Zeit, als Kodierungen unzuverlässig waren.
Warum zeigt mein kodierter Text &lt; statt <?
Etwas hat ihn zweimal kodiert. Der erste Durchgang machte aus < ein <, der zweite sah das & in diesem Ergebnis und machte & daraus. Suche nach einem Template, das einen bereits stromaufwärts maskierten Wert erneut maskiert — häufig, wenn eine Schicht hilfsbereit maskiert und eine andere es "sicherheitshalber" nochmal tut. Maskiere einmal, an der Ausgabestelle.