Kodierung, Escape und Hashing
Base64 ist keine Verschlüsselung, btoa ist nicht UTF-8, encodeURI ist nicht encodeURIComponent und SHA-256 ist kein Passwort-Hash. Hier erfahren Sie, was jede davon tatsächlich bewirkt und welche spezifischen Fehler sich daraus ergeben, wenn man etwas anderes annimmt.
Kodierung ist keine Verschlüsselung und keine Komprimierung
Durch die Kodierung ändert sich die Art und Weise, wie Daten niedergeschrieben werden. Durch die Verschlüsselung ändert sich, wer es lesen kann. Durch die Komprimierung ändert sich der Platzbedarf. Das sind drei verschiedene Jobs, und base64 erledigt nur den ersten – schlecht, wenn Sie auf einen der beiden anderen gehofft haben.
Base64 benötigt jeweils drei Bytes und schreibt sie in vier Zeichen um, die aus einem 64-Symbolalphabet stammen. Vier Zeichen mit drei Bytes bedeuten, dass die Ausgabe immer etwa 33 % größer ist als die Eingabe, zuzüglich Auffüllung. Es existiert, weil ein großer Teil der Infrastruktur – E-Mail-Header, HTTP-Header, JSON-Stringwerte, URLs, XML-Attribute – für Text konzipiert wurde und beliebige Bytes entstellt oder ablehnt. Base64 ist der Adapter, mit dem Sie Bytes durch eine textförmige Pipe übertragen können.
Jeder kann es sofort rückgängig machen, ohne Schlüssel, denn es gibt keinen Schlüssel. Wenn Sie ein Base64-Passwort verwenden, haben Sie das Passwort in einem etwas unpraktischen Format veröffentlicht. Das ist wichtig, weil die Base64-Ausgabe für das menschliche Auge verzerrt aussieht, was genau die Eigenschaft ist, die Menschen dazu bringt, ihr bei Dingen zu vertrauen, die sie nicht kann.
Warum btoa() kaputt geht und auf welche zwei verschiedenen Arten es kaputt geht
Der Browser bietet Ihnen btoa() und atob(), und diese sind älter als die modernen Text-APIs. btoa ist über „binäre Zeichenfolgen“ definiert: Zeichenfolgen, in denen jede Codeeinheit ein einzelnes Byte ist, von 0 bis 255. Text ist das nicht.
Der erste Misserfolg ist laut. Rufen Sie btoa("世界") auf und Sie erhalten einen InvalidCharacterError, da U+4E16 nicht in ein Byte passt. Laute Fehler sind die gute Art – man bemerkt sie sofort und macht sich auf die Suche nach einer Lösung.
Der zweite Fehler ist still und erreicht die Produktion. Das Zeichen é ist U+00E9, was in ein Byte passt. Also kehrt btoa("café") glücklich zurück und kodiert é als einzelnes Byte 0xE9. Aber é in UTF-8 besteht aus zwei Bytes, 0xC3 0xA9. Die Base64, die Sie gerade erstellt haben, decodiert in jedem anderen System der Welt etwas, das nicht Ihr Text ist. Wochen später erfahren Sie, wann aus einem Namen in einer Datenbank ein Ersatzzeichen geworden ist.
Die Lösung besteht darin, Text nicht mehr als Bytes zu behandeln und ihn explizit zu konvertieren. TextEncoder erzeugt das UTF-8 bytes; kodieren Sie diese. TextDecoder wandelt Bytes wieder in Text um, und wenn er mit { fatal: true } erstellt wird, wirft er ungültige Sequenzen an, statt stillschweigend U+FFFD zu ersetzen, sodass eine Dekodierung, die unmöglich richtig sein kann, fehlschlägt, anstatt plausibel aussehenden Unsinn zurückzugeben. Das ist die Pipeline, die dieses Toolkit verwendet, weshalb Emoji Markierungen und Rechts-nach-Links-Skripte kombiniert, die alle hin und her gehen.
- Konvertieren Sie Text mit TextEncoder in Bytes – indizieren Sie niemals in die Zeichenfolge.
- Codieren Sie die Bytes in Base64.
- Umgekehrt: Base64 in Bytes dekodieren, dann die Bytes als UTF-8 mit fatal: true dekodieren.
- Wenn der Schritt UTF-8 fehlschlägt, ist die Nutzlast binär und kein Text. Zeigen Sie es als Verhexung, anstatt so zu tun.
base64 versus base64url und die Füllfrage
Standard-Base64 verwendet + und / als letzte beiden Symbole. Beide sind in URLs von Bedeutung: + kann als codiertes Leerzeichen in Abfragezeichenfolgen gelesen werden und / ist ein Pfadtrennzeichen. Daher definiert RFC 4648 ein zweites Alphabet, base64url, das stattdessen - und _ ersetzt. JWTs verwenden es, ebenso wie die meisten Token-Formate und viele APIs.
Padding ist die andere Variable. Standardmäßige Base64-Pads mit =, sodass die Ausgabelänge immer ein Vielfaches von vier ist. base64url lässt normalerweise die Auffüllung weg, da = selbst ein unangenehmes Zeichen in einer URL ist und die Länge arithmetisch wiederhergestellt werden kann. Ein Decoder, der auf Padding besteht, lehnt vollkommen gültige JWT-Segmente ab.
Praktischer Rat: Ihr Decoder sollte beide Alphabete akzeptieren und fehlende Auffüllung tolerieren, da Sie selten kontrollieren, was Ihnen übergeben wird. Ihr Encoder sollte explizit angeben, was er ausgibt, da es den Empfänger wahrscheinlich interessiert. Das Dienstprogramm base64 hier macht genau das – es akzeptiert alles Vernünftige und lässt Sie genau auswählen, was es produzieren soll.
encodeURI und encodeURIComponent: der Unterschied in einem Satz
Beide prozentual kodieren mit UTF-8. Sie unterscheiden sich nur darin, welche Zeichen sie in Ruhe lassen, und dieser Unterschied macht die ganze Geschichte aus: encodeURIComponent entgeht den reservierten Trennzeichen, encodeURI nicht.
Die reservierten Trennzeichen sind die Zeichen, die einer URL ihre Struktur geben: : /? # [ ] @ ! $ & ' ( ) * + , ; =. encodeURI geht davon aus, dass Sie ihm eine URL übergeben haben, die bereits korrekt strukturiert ist und dies auch bleiben muss, sodass diese beibehalten werden – https:// wird nicht in https%3A%2F%2F umgewandelt. encodeURIComponent geht davon aus, dass Sie ihm ein Teil übergeben haben, das in einen Schlitz fallen gelassen wird, sodass es ihnen entkommt und sicherstellt, dass das Teil nicht aus seinem Schlitz ausbrechen kann.
Der dadurch erzeugte Fehler ist völlig mechanisch. Nehmen Sie einen Suchwert von a&b=c. Codieren Sie es mit encodeURI und hängen Sie es als ?q=a&b=c an, und Sie haben stillschweigend zwei Parameter erstellt: q ist jetzt nur noch "a", und ein verirrtes b=c ist aufgetaucht. Codieren Sie es mit encodeURIComponent und Sie erhalten ?q=a%26b%3Dc, einen Parameter, korrekten Wert. Dieselbe Fehlerklasse ermöglicht es einem manipulierten Wert, Parameter in eine von Ihrem Code erstellte URL einzufügen – weshalb „Verwenden Sie das Komponentenformular für Werte“ eine Sicherheitsregel ist, nicht nur eine Richtigkeitsregel.
Die Formularkodierung ist eine dritte Regel, die wie die zweite aussieht. application/x-www-form-urlencoded schreibt ein Leerzeichen als + statt %20. Wenn Sie einen Formularkörper mit einfachem decodeURIComponent dekodieren, wird jedes Pluszeichen in den Daten zu einem Leerzeichen. Jedes Suchfeld, das jemals „C++“ in „C“ verstümmelt hat, ist dieser Fehler.
HTML-Entitäten und warum das Dekodieren mit innerHTML eine schlechte Angewohnheit ist
Das Escapen für HTML ist eng und gut verständlich: & wird zu &, < wird zu <, > wird zu >, und innerhalb der Attributwerte „ und ' müssen auch Escapezeichen verwendet werden Sicherheit.
Beim Dekodieren lebt die schlechte Angewohnheit weiter. Der einzeilige Trick, der in jeder Antwort erscheint, besteht darin, die Zeichenfolge dem innerHTML eines getrennten Elements zuzuweisen und dessen Textinhalt zurückzulesen. Es funktioniert, und es ist eine schlechte Idee. Sie haben nicht vertrauenswürdige Eingaben an den HTML-Parser übergeben, der daraus echte DOM-Knoten erstellt. Ein <img src=x onerror=...> in dieser Zeichenfolge wird zu einem tatsächlichen Bildelement, an das ein tatsächlicher Fehlerhandler angehängt ist. Wenn dieser Teilbaum jemals in das Dokument eingefügt wird, wird er ausgeführt. Außerdem werden Ihre Daten stillschweigend zerstört: Tags in der Eingabe verschwinden anstelle eines Roundtrips, da der Parser sie als Markup und nicht als Text interpretiert.
Für die ordnungsgemäße Decodierung von Entitäten ist überhaupt kein Parser erforderlich: Vergleichen Sie die Referenz, suchen Sie den Namen in einer Tabelle oder führen Sie die Arithmetik für eine numerische Referenz durch. Das sind ein paar Dutzend Zeilen, es kann nichts ausgeführt werden und es wird zuverlässig ein Roundtrip durchgeführt. Dieses Toolkit macht das auf diese Weise, weshalb Ihnen beim Einfügen eines Skript-Tags in den Entity-Decoder ein Skript-Tag angezeigt wird.
Die Wahl eines Hashs und die drei Fragen, die darüber entscheiden
Ein kryptografischer Hash wandelt jede Eingabe in einen Digest fester Länge um, sodass es unmöglich sein sollte, zwei Eingaben mit demselben Digest zu finden. Durch diese Eigenschaft kann ein Digest die Daten ersetzen – in einer Signatur, einer Integritätsprüfung oder einer Inhaltsadresse.
Erste Frage: Schützen Sie sich vor Unfällen oder vor einem Gegner? Eine Prüfsumme, die vor einem beschädigten Download schützt, muss nur zufällige Überschläge abfangen; CRC32 ist in Ordnung. Ein Digest, von dessen Kollision ein Angreifer profitieren könnte, benötigt einen Hash, der noch vorhanden ist. Diese Unterscheidung ist der Grund, warum SHA-1 nicht einfach „alt“ ist.
SHA-1 ist konkret kaputt. In 2017 erzeugte die SHAttered-Arbeit zwei verschiedene PDF-Dateien mit demselben SHA-1-Digest. In 2020 demonstrierte „SHA-1 is a Shambles“ eine Kollision mit einem ausgewählten Präfix – die stärkere und weitaus gefährlichere Variante, da ein Angreifer dadurch zwei sinnvoll unterschiedliche Dokumente anstelle von zwei sorgfältig konstruierten Blobs kollidieren lässt. Wenn die Sicherheit eines Systems auf der Kollisionsresistenz SHA-1 beruht, ist diese Sicherheit weg. SHA-1 bleibt in diesem Toolkit, da Git-Objekt-IDs und eine lange Reihe älterer API-Signaturen es immer noch verwenden und Sie in der Lage sein müssen, diese Werte zu reproduzieren. Einen Wert zu reproduzieren ist nicht dasselbe wie sich auf ihn zu verlassen.
Zweite Frage: Handelt es sich bei der Eingabe um ein Passwort? Wenn ja, ist keines davon die Antwort. SHA-256 ist auf Schnelligkeit ausgelegt, und schnell ist für Passwörter genau das Falsche: Es bedeutet, dass ein Angreifer mit Ihrer Datenbank Milliarden von Vermutungen pro Sekunde durchführen kann. Passwörter benötigen eine bewusst langsame, speicherintensive Funktion mit einem Salt pro Benutzer – Argon2id, scrypt oder bcrypt. Das ist keine Nuance; Die Verwendung von SHA-256 für Passwörter ist der häufigste schwerwiegende Hashing-Fehler.
Dritte Frage: Benötigen Sie einen verschlüsselten Digest? Wenn Sie eine Nachricht authentifizieren, anstatt sie mit einem Fingerabdruck zu versehen, benötigen Sie HMAC und keinen bloßen Hash. Ein Geheimnis zu verketten und zu hashen ist ein klassisches Eigentor gegen Längenverlängerungsangriffe; HMAC existiert, weil diese Konstruktion schwieriger umzusetzen ist, als sie aussieht.
Für alles andere – Fingerabdrücke einer Datei, einer Inhaltsadresse, eines Integritätsattributs – ist SHA-256 die sinnvolle Standardeinstellung, und SHA-512 ist auf 64-Bit-Hardware oft schneller und bietet gleichzeitig einen breiteren Überblick.
Warum die Hashes hier vom Browser kommen
Die Digests in diesem Toolkit werden von SubtleCrypto, der browsereigenen Web-Crypto-Implementierung, berechnet, nicht von JavaScript, das von dieser Website geliefert wird. Das ist eine bewusste Entscheidung: Die Implementierung des Browsers wird geprüft, gewartet und läuft normalerweise als optimierter nativer Code. Ein handgeschriebener SHA-256 in einem Seitenpaket ist eher vertrauenswürdiger Code ohne Nutzen.
Es hat eine sichtbare Konsequenz. Web Crypto wird nur in einem sicheren Kontext offengelegt, d. h. https:// oder localhost. Öffnen Sie diese Seite über einfaches HTTP an einer LAN-Adresse und crypto.subtle wird undefiniert sein, sodass das Hash-Dienstprogramm Ihnen dies deutlich mitteilt, anstatt stillschweigend zu scheitern oder etwas Schwächeres zu ersetzen.
Die gleiche Argumentation treibt den UUID-Generator an. crypto.randomUUID() ist ebenfalls nur für den sicheren Kontext bestimmt. Wenn es also nicht verfügbar ist, greift das Toolkit auf crypto.getRandomValues() zurück – was immer noch dieselbe kryptografisch sichere Quelle ist – und setzt die Versions- und Variantenbits selbst. Was es niemals tun wird, ist auf Math.random() zurückzugreifen. Dabei handelt es sich um einen schnellen, nicht-kryptografischen PRNG, dessen interner Status anhand einer kurzen Ausgabe seiner Ausgaben wiederhergestellt werden kann, und Bezeichner haben die unglückliche Angewohnheit, zu Sitzungsschlüsseln und Links zum Zurücksetzen von Passwörtern befördert zu werden. Wenn keine sichere Quelle vorhanden ist, generiert dieses Tool nichts und sagt den Grund dafür.
Was passiert mit dem, was Sie einfügen?
- Jede Konvertierung, jeder Hash, jede Dekodierung und jeder Diff wird in Ihrem Browser-Tab ausgeführt. Es werden keine Eingaben hochgeladen, protokolliert oder auf einem Server gespeichert, da nach dem Laden der Seite kein Server beteiligt ist.
- Hashes stammen von der browsereigenen Web Crypto-Implementierung und UUIDs von seinem kryptografisch sicheren Zufallsgenerator. Bei beiden handelt es sich nicht um einen Netzwerkanruf.
- Nichts, was Sie eingeben, wird in den lokalen Speicher oder in ein Cookie geschrieben. Beim Neuladen der Seite wird diese verworfen; Wenn Sie die Registerkarte schließen, wird sie verworfen.
- Siteweite Analysen laufen nur auf dem konfigurierten kanonischen Produktionshost und werden in der Datenschutzrichtlinie offengelegt; Lokale und Vorschau-Hosts lehnen dies ab. Eingefügte Werte, Token, URLs und Dateiinhalte sind von den eigenen Analyseereignissen von ToolAcre ausgeschlossen. Werbung ist in der aktuellen Konfiguration deaktiviert.
- Das heißt: Ein JWT oder ein API-Schlüssel ist ein Live-Zugangsdatensatz. Es ist eine sichere Angewohnheit, niemals etwas in eine Webseite einzufügen, die Sie nicht geschrieben haben, egal wie vertrauenswürdig die Behauptungen sind – auch diese hier.
Fragen
Ist Base64 eine Möglichkeit, Daten zu verbergen?
Nein. Es handelt sich um eine umkehrbare Textdarstellung ohne Schlüssel, die jeder im Bruchteil einer Sekunde entschlüsseln kann. Es sorgt dafür, dass Daten Nur-Text-Kanäle überdauern; es macht es nicht geheim. Alles, was wirklich vertraulich ist, muss verschlüsselt werden, und das verschlüsselte Ergebnis wird dann für den Transport oft base64-codiert – was die Ursache für die Verwirrung ist.
Warum ist mein Base64 länger als die Eingabe?
Da vier Ausgabezeichen drei Eingabebytes enthalten, hat die Ausgabe ungefähr die Größe 4/3 plus bis zu zwei Füllzeichen. Das liegt in der Natur des Formats. Wenn es auf die Größe ankommt, komprimieren Sie vor der Kodierung – niemals danach, da die Base64-Ausgabe schlecht komprimiert wird.
Welche URL-Kodierungsfunktion sollte ich verwenden?
Verwenden Sie encodeURIComponent für jedes einzelne Teil, das Sie in eine URL einfügen: einen Abfragewert, ein Pfadsegment, ein Fragment. Verwenden Sie encodeURI nur, wenn Sie über eine vollständige, bereits strukturierte URL verfügen, die lediglich Leerzeichen oder Nicht-ASCII enthält. Wenn Sie eine Abfragezeichenfolge erstellen, bevorzugen Sie URLSearchParams, das die richtige Regel für Sie anwendet und den Leerzeichen-plus-Unterschied berücksichtigt.
Warum meldet mein Decoder „URI malformed“?
Weil auf ein % in der Eingabe keine zwei hexadezimalen Ziffern folgen. Normalerweise enthält der Text ein wörtliches Prozentzeichen – „50% off“ –, das nie codiert wurde. Ein Literalprozentsatz muss als %25 geschrieben werden. Das URL-Dienstprogramm meldet hier die genaue Position des störenden Escapes, anstatt es einfach abzulehnen.
Kann ich SHA-256 zum Speichern von Passwörtern verwenden?
Nein. SHA-256 ist von Natur aus schnell, was bedeutet, dass ein Angreifer, der Ihre Datenbank stiehlt, Milliarden von Kandidaten-Passwörtern pro Sekunde auf handelsüblicher Hardware testen kann. Passwörter benötigen eine langsame, speicherintensive Salted-Funktion: Argon2id, scrypt oder bcrypt. Dies ist der häufigste schwerwiegende Fehler in diesem Bereich.
Warum ist SHA-1 immer noch hier, wenn es kaputt ist?
Weil Sie immer noch SHA-1-Werte reproduzieren müssen, die bereits vorhanden sind: Git-Objekt-IDs, alte TLS-Zertifikat-Fingerabdrücke, veraltete API-Anforderungssignaturen. Die Möglichkeit, einen Wert für die Interoperabilität zu berechnen, ist etwas anderes, als sich auf ihn für die Sicherheit zu verlassen. Jeder Ort, an dem SHA-1 in diesem Toolkit vorkommt, ist entsprechend gekennzeichnet.
Warum liefern zwei Tools unterschiedliche Hashes für denselben Text?
Fast immer ein Unterschied in den Bytes, nicht im Algorithmus. Die üblichen Schuldigen sind ein nachgestellter Zeilenumbruch (eine Datei endet mit einem Zeilenumbruch, ein Textfeld möglicherweise nicht), eine unterschiedliche Textkodierung oder CRLF- bzw. LF-Zeilenenden. Dieses Tool hasht den UTF-8 bytes genau Ihrer Eingabe und zeigt Ihnen die Bytezahl an, wodurch die Diskrepanz normalerweise offensichtlich wird.
Einschränkungen
- Die benannte HTML-Entitätstabelle deckt die praktische Teilmenge ab – markupkritische Zeichen, Typografie, Währung, Pfeile, Mathematik, Griechisch und Latein – 1 – nicht alle 2,231 benannten HTML5-Referenzen. Nicht erkannte Namen werden gemeldet und genau so belassen, wie sie geschrieben sind, anstatt sie zu erraten.
- Für die Entitätsdekodierung ist das abschließende Semikolon erforderlich. HTML5 toleriert eine Handvoll Legacy-Referenzen ohne solche, aber ihre korrekte Dekodierung hängt vom umgebenden Markup-Kontext ab, über den ein eigenständiges Texttool nicht verfügt.
- Hashing und UUID-Generierung benötigen einen sicheren Kontext (https:// oder localhost), da Web Crypto sonst nicht offengelegt wird. Das Tool meldet dies, anstatt eine schwächere Implementierung zu ersetzen.
- Es sind nur SHA-1, SHA-256, SHA-384 und SHA-512 verfügbar, da diese von SubtleCrypto implementiert werden. MD5 fehlt sowohl aus freien Stücken als auch aus Notwendigkeit.
- Hier gibt es kein HMAC, keine Schlüsselableitung und keine Verschlüsselung. Diese erfordern eine Schlüsselverwaltung, die nicht von einer im Internet gefundenen Seite übernommen werden sollte.
- Alles ist durch den Speicher Ihres Geräts begrenzt, da alles in einem Browser-Tab ausgeführt wird. Die Eingaben sind begrenzt – ein paar Megabyte pro Dienstprogramm – und das Tool lehnt übergroße Arbeiten ab, anstatt sie einzufrieren.