Base64, URL-Kodierung und JWT erklärt
Diese drei begegnen Ihnen überall dort, wo Daten als Text unterwegs sind: in URLs, HTTP-Headern, JSON und Anmelde-Tokens. Keines davon verbirgt etwas. Diese Anleitung erklärt, wie jedes funktioniert, wo Sie ihm begegnen und welche Fehler hinter den meisten Problemen stecken.
Kodieren ist nicht Verschlüsseln
Beim Kodieren ändert sich die Schreibweise der Daten, damit sie einen Kanal passieren, der nur bestimmte Zeichen erlaubt. Jeder kann das rückgängig machen, denn es ist kein Schlüssel im Spiel. Auch Verschlüsselung verändert die Daten, aber nur wer den Schlüssel hat, kann sie zurückverwandeln. Ein Hash ist wieder etwas anderes: ein Einweg-Fingerabdruck, der sich überhaupt nicht zurückrechnen lässt, wie in Hashfunktionen erklärt beschrieben.
Base64 und URL-Kodierung sind Kodierungen. Ein JWT ist meist signiert; das belegt, wer ihn ausgestellt hat und dass er nicht verändert wurde, sein Inhalt ist aber nur kodiert. Muss ein Wert geheim bleiben, braucht er Verschlüsselung, nicht eines dieser Formate.
So funktioniert Base64
Base64 liest die Daten in Gruppen von drei Bytes (24 Bit) und teilt sie in vier Gruppen zu je sechs Bit. Jede Gruppe ist eine Zahl von 0 bis 63 und wird als ein Zeichen eines Alphabets mit 64 Zeichen geschrieben: A–Z für 0–25, a–z für 26–51, 0–9 für 52–61, dann + und /. Das Wort „Man“ zeigt die Schritte:
| Schritt | M | a | n |
|---|---|---|---|
| Bytewerte | 77 | 97 | 110 |
| Bits | 01001101 | 01100001 | 01101110 |
| Vier 6-Bit-Gruppen | 010011 · 010110 · 000101 · 101110 = 19 · 22 · 5 · 46 | ||
| Base64 | T W F u → TWFu | ||
Ist die Länge der Eingabe kein Vielfaches von drei, wird die letzte Gruppe aufgefüllt und mit Padding markiert: Ein übriges Byte ergibt zwei Zeichen und ==, zwei übrige Bytes ergeben drei Zeichen und =. „Ma“ wird zu TWE= und „M“ zu TQ==.
Weil aus je drei Bytes vier Zeichen werden, macht Base64 die Daten um etwa ein Drittel größer, dazu kommen Zeilenumbrüche, wo ein Format sie einfügt. Ein 3-MB-Foto als E-Mail-Anhang reist als rund 4 MB Text, und ein als Data-URL in ein Stylesheet eingebettetes Bild ist etwa ein Drittel schwerer als die Datei.
Wo Ihnen Base64 begegnet
- E-Mail-Anhänge (MIME) sind Base64-Text, umbrochen nach 76 Zeichen pro Zeile.
- Data-URLs wie
data:image/png;base64,iVBORw0…betten eine kleine Datei direkt in HTML oder CSS ein. - HTTP-Basic-Authentifizierung sendet
user:passwordals Base64 im Authorization-Header. Das verbirgt das Passwort nur vor flüchtigen Blicken und ist deshalb nur über HTTPS sicher. - Zertifikate und Schlüssel in PEM-Dateien sind Base64 zwischen den Zeilen
-----BEGIN …-----und-----END …-----, umbrochen nach 64 Zeichen. - JSON und XML tragen Binärwerte wie eine Signatur oder ein Vorschaubild als Base64-Zeichenketten, weil sie keinen Binärtyp haben.
Base64URL und Padding
Das + und der / des Standard-Base64 haben in URLs und Dateipfaden eine Bedeutung, daher verwendet ein zweites Alphabet, Base64URL, stattdessen - und _. Meist lässt es auch das =-Padding weg, da der Decoder die Länge berechnen kann. JWTs, viele API-Tokens und manche Dateinamen nutzen Base64URL. Um Standard-Base64 in Base64URL umzuwandeln, ersetzen Sie + durch -, / durch _ und entfernen abschließende =; für den Rückweg kehren Sie die Ersetzungen um und hängen = an, bis die Länge ein Vielfaches von vier ist.
Prozentkodierung in URLs
Eine URL darf nur eine begrenzte Menge an Zeichen enthalten, und einige davon strukturieren die Adresse: / und ? trennen Pfad und Abfrage, & und = trennen Parameter, # leitet das Fragment ein. Enthält ein Wert eines dieser Zeichen oder irgendetwas außerhalb von ASCII, wird er prozentkodiert: Jedes Byte seiner UTF-8-Form wird als % mit zwei Hexadezimalziffern geschrieben. „café“ wird zu caf%C3%A9, weil é aus den zwei Bytes C3 A9 besteht.
Buchstaben, Ziffern und - . _ ~ müssen nie kodiert werden. Diese Zeichen verursachen am häufigsten Probleme:
| Zeichen | Kodiert | Warum es wichtig ist |
|---|---|---|
| Leerzeichen | %20 | In einer URL nicht erlaubt |
| & | %26 | Würde einen neuen Parameter beginnen |
| = | %3D | Würde den Parameternamen beenden |
| ? | %3F | Würde die Abfrage beginnen |
| # | %23 | Würde das Fragment beginnen, das nicht an den Server gesendet wird |
| / | %2F | Würde ein Pfadsegment hinzufügen |
| + | %2B | Wird in Formulardaten als Leerzeichen gelesen |
| % | %25 | Würde eine Escape-Sequenz beginnen |
Kodieren Sie jeden Wert einzeln und setzen Sie dann die URL zusammen. In JavaScript erledigt encodeURIComponent das für einen Wert und kodiert auch / ? & = #, während encodeURI für eine ganze Adresse gedacht ist und diese Zeichen unverändert lässt. encodeURI auf einen einzelnen Wert anzuwenden, ist eine häufige Ursache für kaputte Abfragezeichenketten.
Pluszeichen, Leerzeichen und doppelte Kodierung
HTML-Formulare senden ihre Felder als application/x-www-form-urlencoded, wobei ein Leerzeichen zu + wird. In der allgemeinen URL-Syntax ist + einfach ein Pluszeichen. Eine Suche nach „C++“, gesendet als q=C++, kommt daher bei einem Server, der Formulardaten dekodiert, als „C“ mit zwei Leerzeichen an; richtig wäre q=C%2B%2B. Wenn Sie eine Abfragezeichenkette von Hand dekodieren, finden Sie zuerst heraus, nach welcher Regel sie entstanden ist.
Doppelte Kodierung entsteht, wenn ein bereits kodierter Wert erneut kodiert wird: Aus %20 wird %2520, weil das % selbst zu %25 wird. Sehen Sie in einer URL %25 gefolgt von zwei Hexadezimalziffern, hat irgendein Schritt der Kette doppelt kodiert; dekodieren Sie einmal und suchen Sie den überzähligen Schritt.
Was in einem JWT steht
Ein JSON Web Token (JWT) transportiert Angaben (Claims), etwa wer der Benutzer ist und bis wann das Token gilt, von dem System, das es ausstellt, zu den Systemen, die es akzeptieren. Die übliche signierte Form besteht aus drei Base64URL-Teilen, getrennt durch Punkte: Header, Payload und Signatur. Dekodiert sind die ersten beiden normales JSON:
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"sub": "user-42", "role": "editor", "iat": 1767225600, "exp": 1767229200}
Der Standard (RFC 7519) legt einige registrierte Claims fest; Anwendungen ergänzen eigene wie role oben.
| Claim | Bedeutung |
|---|---|
| iss | Aussteller: wer das Token erstellt hat |
| sub | Subjekt: meist die Benutzer- oder Konto-ID |
| aud | Zielgruppe: der Dienst, für den das Token bestimmt ist |
| exp, nbf, iat | Ablauf, nicht vor, ausgestellt am: Unix-Zeit in Sekunden |
| jti | Token-ID, um Wiederverwendung zu erkennen oder ein einzelnes Token zu widerrufen |
Da die Payload nur kodiert ist, kann jeder sie lesen, der das Token besitzt. Halten Sie personenbezogene Daten und Geheimnisse heraus, und halten Sie Tokens klein: Sie werden mit jeder Anfrage mitgeschickt, und viele Server begrenzen die Größe von Anfrage-Headern.
Signiert heißt nicht geheim: wie ein JWT geprüft wird
Die Signatur wird über Header und Payload berechnet. Bei HS256 teilen Aussteller und Empfänger ein Geheimnis und verwenden HMAC mit SHA-256. Bei RS256 oder ES256 signiert der Aussteller mit einem privaten Schlüssel, und Empfänger prüfen mit dem passenden öffentlichen Schlüssel, sodass sie selbst keine Tokens erzeugen können.
Ein empfangender Server muss die Signatur mit dem erwarteten Algorithmus und Schlüssel prüfen sowie exp, nbf, iss und aud kontrollieren, bevor er irgendeinem Claim vertraut. Reines Dekodieren, wie es das LeedyBox-Werkzeug tut, überspringt all das: Es zeigt, was ein Token behauptet, nicht ob es echt ist. Bibliotheken müssen außerdem den Algorithmus „none“ ablehnen, der ein unsigniertes Token kennzeichnet, sofern unsignierte Tokens nicht ausdrücklich vorgesehen sind. Muss der Inhalt selbst verborgen bleiben, wird das Token stattdessen verschlüsselt (JWE); es hat dann fünf Teile, die sich ohne Schlüssel nicht lesen lassen.
Häufige Probleme und Lösungen
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| Base64 ergibt beim Dekodieren Zeichensalat | Die Daten sind binär oder Text in einer anderen Kodierung | Prüfen, was kodiert wurde; Binärdaten stattdessen als Datei speichern |
| „Ungültiges Zeichen“ beim Dekodieren | Ein data:-Präfix, Anführungszeichen oder das Base64URL-Alphabet | Alles vor dem Komma entfernen; einen Decoder verwenden, der - und _ akzeptiert |
| Ein Parameter kommt abgeschnitten an | Ein unkodiertes & oder # im Wert | Jeden Wert mit encodeURIComponent kodieren |
| %2520 in einer URL | Der Wert wurde doppelt kodiert | Einmal kodieren, an der Stelle, an der die URL gebaut wird |
| Token abgelehnt, obwohl exp in der Zukunft liegt | Falsche Zielgruppe oder falscher Aussteller, Uhrabweichung oder abweichender Schlüssel | aud, iss und den Signaturschlüssel mit den Erwartungen des Servers vergleichen |
| Ablaufdatum Tausende Jahre in der Zukunft | exp in Millisekunden statt Sekunden geschrieben | exp, nbf und iat in Sekunden speichern |
Häufig gestellte Fragen
Schützt Base64 ein Passwort oder einen API-Schlüssel?
Nein. Base64 lässt sich von jedem in einer Sekunde rückgängig machen. Ein in Base64 kodierter Zugangsschlüssel, etwa in einer Konfigurationsdatei oder einer URL, ist so offen wie Klartext.
Darf ich personenbezogene Daten in die Payload eines JWT schreiben?
Besser nicht. Jeder, der das Token hat, kann die Payload lesen, auch Browsererweiterungen, Protokolle und Proxys, die die Anfrage sehen. Legen Sie eine ID ins Token und rufen Sie die Details auf dem Server ab.
Schützt URL-Kodierung vor Injection-Angriffen?
Nur insofern, als ein Wert die Struktur der URL nicht zerstören kann. Der Server muss den dekodierten Wert trotzdem für den Ort prüfen und maskieren, an dem er ihn verwendet, etwa SQL, HTML oder einen Shell-Befehl.
Warum ist meine Base64-Zeichenkette länger als die Datei?
Aus je drei Bytes werden vier Zeichen, der Text ist also etwa 33 % größer, und Zeilenumbrüche kommen noch hinzu. Bei großen Dateien ist es effizienter, die Bytes direkt zu senden statt Base64 zu verwenden.