JWT dekodieren und debuggen
Ein JSON Web Token besteht aus nur drei Base64URL-kodierten Teilen. Sobald du sie lesen kannst, erklären sich die meisten Fehler wie „invalid token“ in Sekunden von selbst. Dieser Guide zeigt dir, wie du einen JWT dekodierst, welche Claims am häufigsten Probleme machen und welche Fehler aus JWTs Sicherheitslücken machen.
Aus welchen Teilen ein JWT besteht
Ein JWT sieht etwa so aus: xxxxx.yyyyy.zzzzz. Die drei Teile, getrennt durch Punkte, sind:
- Header — JSON mit dem Signaturalgorithmus (
alg) und dem Token-Typ, zum Beispiel{"alg":"HS256","typ":"JWT"}. - Payload — JSON mit den Claims: wem das Token gehört und wann es gültig ist.
- Signatur — ein Hash der ersten beiden Teile, erstellt mit einem Secret (HMAC) oder einem privaten Schlüssel (RSA, ECDSA).
Die ersten beiden Teile sind nur kodiert, nicht verschlüsselt. Jeder, der das Token hat, kann sie lesen. Deshalb solltest du niemals Passwörter oder personenbezogene Daten in den JWT-Payload schreiben.
Token dekodieren
Füge das Token in den JWT Decoder ein. Er zeigt dir Header, Payload und alle Zeitstempel als lesbare Datumsangaben. Das Tool läuft in deinem Browser, deshalb werden Produktions-Tokens nirgendwo hingeschickt. Willst du es manuell machen, dekodiere jeden Teil mit Base64URL: ersetze - durch +, _ durch / und ergänze das Padding. Oder in Node:
const [header, payload] = token.split('.').slice(0, 2)
.map(part => JSON.parse(Buffer.from(part, 'base64url').toString()));
console.log(header, payload);
Die Claims, die am häufigsten Probleme machen
| Claim | Bedeutung | Typischer Fehler |
|---|---|---|
exp | Ablaufzeit, Unix-Sekunden | „jwt expired“ — prüfe auch die Uhren von Server und Client |
nbf | Nicht gültig vor | Ein Token wurde ein paar Sekunden zu früh verwendet, weil die Uhr des Ausstellers vorgeht |
iat | Ausgestellt am | Wird nach einer Zeitabweichung als „issued in the future“ abgelehnt |
aud | Zielgruppe (Audience) des Tokens | Ein Token für eine API wurde an eine andere geschickt — der Prüfer muss das kontrollieren |
iss | Aussteller (Issuer) | Dev- und Produktions-Aussteller werden verwechselt |
Zeitstempel sind in Sekunden angegeben, nicht in Millisekunden. Sieht eine Ablaufzeit nach dem Jahr 55.000 aus, wurde vermutlich Date.now() statt Math.floor(Date.now() / 1000) verwendet. Der Unix Timestamp Converter klärt das schnell. Die meisten Bibliotheken akzeptieren eine kleine Clock Tolerance (30–60 Sekunden), um Zeitabweichungen zwischen Servern abzufangen.
Warum „invalid signature“?
- Falsches Secret oder falscher Schlüssel — die häufigste Ursache. Prüfe die Umgebungsvariable auf dem Server, der die Signatur verifiziert, nicht nur auf dem, der sie erstellt.
- Falscher Algorithmus — ein mit RS256 signiertes Token wird als HS256 geprüft, oder umgekehrt.
- Das Token wurde verändert — ein einziges geändertes Zeichen im Payload bricht die Signatur, und genau das ist der Sinn.
- Schlüsselrotation — die Schlüssel des Ausstellers haben sich geändert. Bei JWKS ordnest du die
kidim Header einem veröffentlichten Schlüssel zu.
Um ein HS256-Token manuell zu prüfen, berechne HMAC-SHA256 über header.payload mit dem Secret im HMAC Generator und vergleiche das Ergebnis, Base64URL-kodiert, mit dem dritten Teil.
Sicherheitsfehler, die du vermeiden solltest
- Dekodieren ist nicht Verifizieren. Vertraue niemals Claims aus
jwt.decode(); nutze die Verify-Funktion deiner Bibliothek mit einer expliziten Liste erlaubter Algorithmen. alg: noneablehnen und den Algorithmus festlegen — das Token darf niemals selbst bestimmen, wie es geprüft wird.- Tokens kurzlebig halten (bei Access-Tokens Minuten) und für lange Sessions Refresh-Tokens verwenden. Ein JWT lässt sich ohne zusätzlichen Server-Status vor Ablauf nicht widerrufen.
- Tokens nicht im localStorage speichern, wenn ein XSS-Fehler sie auslesen könnte. Für Browser-Apps ist ein HttpOnly-, Secure- und SameSite-Cookie sicherer.
Brauchst du ein Test-Token mit bestimmten Claims? Erstelle eines mit dem JWT Encoder.