JWT Tokens Explained: Structure, Security & Decoding
Understand JSON Web Tokens from the inside out. Learn the header, payload, and signature structure, common claims, signing algorithms, and security best practices.
Was ist ein JWT?
Ein JSON Web Token (JWT, ausgesprochen „jot“) ist eine kompakte, URL-sichere Zeichenkette, die Claims zwischen zwei Parteien darstellt. Es ist die gängigste Methode für Authentifizierung und Autorisierung in modernen Webanwendungen.
Wenn Sie sich bei einer Web-App anmelden und der Server Ihnen ein Token zurückgibt, anstatt eine serverseitige Session anzulegen, handelt es sich mit großer Wahrscheinlichkeit um ein JWT. Ihr Browser sendet es bei jeder weiteren Anfrage mit, und der Server prüft die Signatur, ohne die Datenbank abzufragen.
Wo JWTs eingesetzt werden
- Authentifizierung. Nach der Anmeldung stellt der Server ein JWT aus. Der Client speichert es und sendet es bei jeder Anfrage im Header
Authorizationmit. - Single Sign-On (SSO). JWTs ermöglichen es Nutzern, sich einmal anzumelden und mehrere Dienste zu nutzen. Identitätsanbieter wie Auth0, Okta und Keycloak stellen JWTs aus, die nachgelagerte Dienste eigenständig verifizieren können.
- API-Autorisierung. Microservices reichen JWTs untereinander weiter, um nachzuweisen, dass eine Anfrage im Namen eines authentifizierten Nutzers mit bestimmten Berechtigungen erfolgte.
- Informationsaustausch. Da JWTs signiert sind, können sie vertrauenswürdige Daten zwischen Parteien transportieren, ohne einen zusätzlichen Verifizierungsschritt.
Die drei Teile eines JWT
Jedes JWT besteht aus drei Base64URL-kodierten Teilen, die durch Punkte getrennt sind:
header.payload.signature
Hier ist ein echtes JWT (zur besseren Lesbarkeit gekürzt):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Dekodieren wir jeden Teil.
Teil 1: Header
Der Header beschreibt den Tokentyp und den Signaturalgorithmus. Aus Base64URL dekodiert:
{ "alg": "HS256", "typ": "JWT" }
alg teilt dem Verifizierer mit, welcher Algorithmus zu verwenden ist. typ bestätigt, dass es sich um ein JWT handelt.
Teil 2: Payload (Claims)
Der Payload enthält die Claims — die eigentlichen Daten, die das Token transportiert. Dekodiert:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
Claims sind Schlüssel-Wert-Paare. Einige sind standardisiert („registered claims“), eigene Claims können ergänzt werden.
Der Payload ist Base64URL-kodiert, nicht verschlüsselt. Jeder, der das Token hat, kann den Payload dekodieren und lesen. Legen Sie niemals Geheimnisse, Passwörter oder sensible personenbezogene Daten in einen JWT-Payload.
Teil 3: Signatur
Die Signatur wird berechnet, indem der kodierte Header, ein Punkt, der kodierte Payload genommen und mit einem geheimen Schlüssel signiert wird:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Die Signatur garantiert, dass das Token nicht manipuliert wurde.
Selbst ausprobieren: Fügen Sie ein beliebiges JWT in unseren JWT Decoder ein, um alle drei Teile sofort zu inspizieren.
Häufige JWT-Claims
| Claim | Vollständiger Name | Beschreibung |
|---|---|---|
sub | Subject | Worum es im Token geht (meist eine Nutzer-ID) |
iss | Issuer | Wer das Token ausgestellt hat (z. B. die URL Ihres Auth-Servers) |
aud | Audience | Vorgesehener Empfänger (z. B. die URL Ihrer API) |
exp | Expiration Time | Unix-Zeitstempel, nach dem das Token ungültig ist |
iat | Issued At | Unix-Zeitstempel der Token-Erstellung |
nbf | Not Before | Token ist vor diesem Unix-Zeitstempel ungültig |
jti | JWT ID | Eindeutige Kennung zur Verhinderung der Token-Wiederverwendung |
Eigene Claims sind alles, was Sie zusätzlich definieren. Halten Sie eigene Claims schlank.
Signaturalgorithmen im Vergleich
| Algorithmus | Typ | Schlüssel | Am besten geeignet für |
|---|---|---|---|
HS256 | Symmetrisch (HMAC) | Ein gemeinsames Geheimnis | Einfache Setups, bei denen Aussteller und Verifizierer derselbe Dienst sind |
RS256 | Asymmetrisch (RSA) | Privater Schlüssel signiert, öffentlicher verifiziert | Verteilte Systeme, SSO |
ES256 | Asymmetrisch (ECDSA) | Privater Schlüssel signiert, öffentlicher verifiziert | Wie RS256, aber kleinere Schlüssel und schneller |
Symmetrisch (HS256) ist einfacher: Beide Seiten teilen dasselbe Geheimnis. Asymmetrisch (RS256, ES256) eignet sich besser für verteilte Architekturen.
Sicherheits-Best Practices
- Niemals Geheimnisse im Payload ablegen. Verwenden Sie JWE, wenn Sie verschlüsselte Tokens benötigen.
- Kurze Gültigkeitsdauer nutzen. 5–15 Minuten für Access-Tokens.
- Immer HTTPS verwenden.
- Alle Claims validieren. Prüfen Sie stets
exp,issundaud. alg: nonenicht akzeptieren.- Eine Positivliste für Algorithmen verwenden.
- Tokens sicher speichern.
HttpOnly-Cookies sind sicherer alslocalStorage.
Typische JWT-Fehler
- JWT in der URL. Stattdessen den Header
Authorizationoder ein Cookie verwenden. - Signatur gar nicht prüfen. Ein Angreifer kann beliebige Tokens fälschen.
- Schwaches Geheimnis für HS256. Verwenden Sie eine kryptografisch zufällige Zeichenkette mit mindestens 256 Bit.
- Tokens ohne Ablauf. Ohne Schlüsselwechsel keine sinnvolle Widerrufsmöglichkeit.
- Zu viele Daten im Token. Ein 4-KB-Token belastet jeden API-Aufruf.
Ablauf Token-Refresh
- Nutzer meldet sich an. Der Server liefert Access-Token (kurzlebig, z. B. 15 Minuten) und Refresh-Token (langlebig, z. B. 7 Tage).
- Der Client sendet das Access-Token bei jeder API-Anfrage im Header
Authorization: Bearer. - Wenn das Access-Token abläuft, antwortet die API mit 401.
- Der Client sendet das Refresh-Token an den Endpunkt
/refresh. - Der Server validiert und stellt ein neues Access-Token aus (optional neues Refresh-Token — Refresh-Token-Rotation).
- Der Client wiederholt die Anfrage mit dem neuen Access-Token.
// Simplified refresh flow (client-side)
const handleApiRequest = async (url, options) => {
let response = await fetch(url, {
...options,
headers: { ...options.headers, Authorization: `Bearer ${getAccessToken()}` },
});
if (response.status === 401) {
const refreshResponse = await fetch("/api/refresh", {
method: "POST",
body: JSON.stringify({ refresh_token: getRefreshToken() }),
});
if (refreshResponse.ok) {
const { access_token, refresh_token } = await refreshResponse.json();
saveTokens(access_token, refresh_token);
response = await fetch(url, {
...options,
headers: { ...options.headers, Authorization: `Bearer ${access_token}` },
});
} else { redirectToLogin(); }
}
return response;
};
JWTs vs. Sessions: Wann was nutzen
| Aspekt | JWT | Serverseitige Sessions |
|---|---|---|
| Zustandsspeicherung | Client (zustandsloser Server) | Server (Session-Store / Datenbank) |
| Skalierbarkeit | Einfach — kein geteilter Zustand zwischen Servern | Erfordert gemeinsamen Session-Store (Redis, DB) |
| Widerruf | Schwer — Blockliste oder kurze Laufzeit nötig | Einfach — Session im Store löschen |
| Payload-Größe | Größer (Claims in jeder Anfrage) | Kleiner (nur Session-ID-Cookie) |
| Cross-Domain | Gut geeignet (als Bearer-Token) | Erfordert CORS-/Cookie-Konfiguration |
| Microservices | Ideal — jeder Dienst verifiziert eigenständig | Jeder Dienst muss den Session-Store abfragen |
| Einfachheit | Mehr Bausteine (Signierung, Refresh-Flow) | Einfacher korrekt umzusetzen |
JWTs nutzen für zustandslose Authentifizierung über verteilte Dienste, mobile Apps oder Drittanbieter-API-Nutzer. Sessions nutzen für einfachen Widerruf, einzelne monolithische Apps oder die einfachste sichere Variante.
Selbst ausprobieren: Dekodieren und inspizieren Sie jedes JWT sofort mit unserem JWT Decoder — keine Daten verlassen Ihren Browser.