Einführung: Was ist ein JSON Web Token?
JSON Web Token (JWT) ist ein offener Standard (RFC 7519), der eine kompakte, in sich geschlossene Methode zur sicheren Übertragung von Informationen zwischen Parteien als JSON-Objekt definiert. Da JWT digital signiert ist, können die darin enthaltenen Informationen verifiziert und als vertrauenswürdig eingestuft werden. JWTs können mit einem Geheimnis (HMAC-Algorithmus) oder einem öffentlichen/privaten Schlüsselpaar (RSA oder ECDSA) signiert werden.
JWTs entstanden, um eine häufige Herausforderung in verteilten Systemen zu lösen: Wie kann ein Server einer Anfrage eines Clients vertrauen, ohne eine Sitzung in einer Datenbank nachschlagen zu müssen? Die Antwort lautet: Die Identität und Berechtigungen des Benutzers werden direkt in ein Token codiert und signiert, das der Client bei jeder Anfrage mitführt.
Heute sind JWTs das Rückgrat moderner Authentifizierung und Autorisierung:
- Single Sign-On (SSO): Eine Anmeldung gewährt Zugriff auf mehrere Dienste.
- API-Authentifizierung: Mobile Apps und SPAs authentifizieren API-Aufrufe mit einem Bearer-Token.
- OAuth 2.0 / OpenID Connect: JWTs werden als Access Tokens und ID Tokens verwendet.
- Microservices: Dienste verifizieren die Identität des Aufrufers ohne zentralen Sitzungsspeicher.
JWT-Struktur: header.payload.signature
Ein JWT ist eine Zeichenkette aus drei Base64url-codierten Teilen, getrennt durch Punkte (.):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Teil 1 — Header dekodiert zu:
{
"alg": "HS256",
"typ": "JWT"
}
Der Header gibt den Signaturalgorithmus (alg) und den Token-Typ (typ) an.
Teil 2 — Payload dekodiert zu:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Der Payload enthält die Claims — Aussagen über die Entität (meist ein Benutzer) und zusätzliche Metadaten.
Teil 3 — Signature wird (für HS256) wie folgt berechnet:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Die Signatur stellt sicher, dass der Token nicht manipuliert wurde. Wichtig: Der Payload ist lediglich Base64url-kodiert, nicht verschlüsselt. Jeder, der einen JWT erhält, kann alle Claims lesen.
Signaturalgorithmen: HS256, RS256 und ES256
HS256 (HMAC-SHA256) — Symmetrisch
Verwendet ein einzelnes gemeinsames Geheimnis sowohl für die Signierung als auch für die Verifikation. Einfach zu implementieren, aber das Geheimnis muss sowohl dem Aussteller als auch dem Verifizierer bekannt sein.
- Geeignet für: Monolithische Anwendungen oder eng gekoppelte Dienste, die ein Geheimnis teilen.
- Risiko: Wenn das Geheimnis durchsickert, können alle Tokens gefälscht werden.
RS256 (RSA-SHA256) — Asymmetrisch
Verwendet einen privaten Schlüssel zum Signieren und einen öffentlichen Schlüssel zur Verifikation. Der private Schlüssel bleibt geheim; der öffentliche Schlüssel kann frei geteilt werden (z. B. über einen JWKS-Endpunkt).
- Geeignet für: Verteilte Systeme, Microservices und Szenarien, in denen mehrere Dienste Tokens verifizieren.
- Schlüsselgröße: Mindestens 2048-Bit-RSA-Schlüssel empfohlen.
ES256 (ECDSA-SHA256) — Asymmetrisch
Verwendet den Elliptic Curve Digital Signature Algorithm mit der P-256-Kurve. Erzeugt kleinere Signaturen als RS256 bei gleichwertiger Sicherheit.
- Geeignet für: Leistungsempfindliche oder bandbreitenbeschränkte Umgebungen.
| Algorithmus | Typ | Schlüssel | Signaturgröße | Anwendungsfall |
|---|---|---|---|---|
| HS256 | Symmetrisch | Gemeinsames Geheimnis | 32 Byte | Einzeldienst-Apps |
| RS256 | Asymmetrisch | RSA-Schlüsselpaar | 256+ Byte | Verteilte Systeme |
| ES256 | Asymmetrisch | EC-Schlüsselpaar | 64 Byte | Hochleistungs-APIs |
JWT-Claims: Registrierte, öffentliche und private
Claims sind Aussagen über eine Entität (meist ein Benutzer) und Metadaten, die im Payload kodiert sind.
Registrierte Claims (IANA-definiert)
| Claim | Vollständiger Name | Beschreibung |
|---|---|---|
iss |
Issuer | Wer den Token ausgestellt hat (z. B. "auth.example.com") |
sub |
Subject | Auf wen sich der Token bezieht (z. B. eine Benutzer-ID) |
aud |
Audience | Für wen der Token bestimmt ist (Dienstkennung) |
exp |
Expiration | Unix-Zeitstempel, nach dem der Token ungültig ist |
nbf |
Not Before | Der Token darf vor diesem Zeitstempel nicht verwendet werden |
iat |
Issued At | Unix-Zeitstempel der Token-Ausstellung |
jti |
JWT ID | Eindeutiger Bezeichner zur Verhinderung von Replay-Angriffen |
Öffentliche Claims
Im IANA JWT Claims Registry registriert. Beispiele: email, name, picture, roles.
Private Claims
Benutzerdefinierte Claims, die zwischen Aussteller und Verbraucher vereinbart werden. Verwende namespaced Namen, um Kollisionen zu vermeiden:
{
"https://example.com/tenant_id": "acme-corp",
"https://example.com/roles": ["admin", "editor"]
}
Wie der JWT-Authentifizierungsfluss funktioniert
- Benutzeranmeldung: Der Client sendet Zugangsdaten (Benutzername + Passwort) an den Authentifizierungsserver.
- Token-Ausstellung: Der Server validiert die Zugangsdaten, erstellt einen signierten JWT mit geeigneten Claims und gibt ihn an den Client zurück.
- Token-Speicherung: Der Client speichert den JWT (im Speicher, im localStorage oder in einem HttpOnly-Cookie).
- Authentifizierte Anfragen: Für jede Anfrage an eine geschützte Ressource fügt der Client den JWT im
Authorization-Header ein:Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - Token-Verifikation: Der Server extrahiert den Token, verifiziert die Signatur, prüft
expund gewährt oder verweigert den Zugriff basierend auf den Claims. - Token-Erneuerung: Wenn das Access Token abläuft, verwendet der Client ein langlebiges Refresh Token, um ohne erneute Anmeldung ein neues Access Token zu erhalten.
Client Auth-Server Ressourcen-Server
| | |
|-- POST /login ---->| |
|<-- 200 + JWT ------| |
| | |
|-- GET /api (Authorization: Bearer JWT) ->|
| | JWT verifizieren |
|<----------- 200 + Daten ---------------->|
Sicherheitsüberlegungen
Der Payload ist NICHT verschlüsselt
Base64url-Kodierung ist keine Verschlüsselung. Jeder, der einen JWT besitzt, kann alle Claims sofort dekodieren und lesen. Niemals in einem JWT-Payload speichern:
- Passwörter oder Passwort-Hashes
- Kreditkartennummern oder andere Finanzdaten
- Sensible personenbezogene Daten (PII) über das Notwendige hinaus
- Private Schlüssel oder interne Geheimnisse
Signatur immer verifizieren
// Node.js — jsonwebtoken-Bibliothek
const jwt = require("jsonwebtoken");
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded.sub); // Vertrauenswürdige Benutzer-ID
} catch (err) {
res.status(401).json({ error: "Ungültiger oder abgelaufener Token" });
}
Kurze Ablaufzeiten verwenden
Access Tokens sollten kurze Lebensdauern haben (15 Minuten bis 1 Stunde). Verwende Refresh Tokens für langlebige Sitzungen, um den Schaden bei einem Token-Diebstahl zu begrenzen.
Immer HTTPS verwenden
Übertrage Tokens niemals über unverschlüsselte Verbindungen. Ein abgefangener Token ist so gefährlich wie ein gestohlenes Passwort.
Häufige Schwachstellen
1. Algorithmus-Verwechslungsangriff (CVE-2015-9235)
Ein Angreifer ändert das Feld alg im Header von RS256 auf HS256 und signiert den Token mit dem öffentlichen Schlüssel des Servers als HMAC-Geheimnis. Ein schlecht implementierter Server akzeptiert den gefälschten Token.
Gegenmaßnahme: Gib den erwarteten Algorithmus auf der Serverseite immer explizit an:
jwt.verify(token, publicKey, { algorithms: ["RS256"] });
2. Der "none"-Algorithmus-Angriff
Wenn ein Server alg: "none" akzeptiert, kann ein Angreifer Tokens ohne Signatur fälschen.
Gegenmaßnahme: Lehne Tokens mit alg: "none" explizit ab. Moderne Bibliotheken haben dieses Problem behoben, aber ältere Versionen können noch anfällig sein.
3. Schwache Geheimnisse (HS256)
Ein kurzes oder leicht zu erratendes HMAC-Geheimnis kann mit Tools wie hashcat offline durch Brute-Force gegen einen abgefangenen Token geknackt werden.
Gegenmaßnahme: Verwende ein kryptografisch zufälliges Geheimnis mit mindestens 256 Bit:
openssl rand -hex 32
4. Fehlende Claim-Validierung
Das Versäumnis, iss, aud oder nbf zu validieren, ermöglicht die dienstübergreifende Wiederverwendung von Tokens oder deren vorzeitigen Einsatz.
5. Unsichere Token-Speicherung
localStorage ist für jedes JavaScript auf der Seite zugänglich und macht Tokens anfällig für XSS-Angriffe. Bevorzuge HttpOnly-Cookies für sensible Tokens.
JWT vs. Session-Tokens: Zustandslos vs. Zustandsbehaftet
| Merkmal | JWT (Zustandslos) | Session-Token (Zustandsbehaftet) |
|---|---|---|
| Speicherort | Client-seitig | Server-seitig (DB oder Cache) |
| Skalierbarkeit | Ausgezeichnet — kein Server-Lookup nötig | Erfordert gemeinsamen Sitzungsspeicher |
| Widerruf | Schwierig — gültig bis exp |
Einfach — aus dem Speicher löschen |
| Token-Größe | Größer pro Anfrage | Kleines Token, Daten auf dem Server |
| Microservices | Ideal — unabhängige Verifikation | Erfordert gemeinsame Infrastruktur |
| Payload-Sichtbarkeit | Jeder mit dem Token kann es lesen | Daten verbleiben server-seitig |
Wähle JWT, wenn du verteilte Systeme, zustandslose REST-APIs oder mobile Apps baust. Wähle Sessions, wenn du sofortigen Widerruf benötigst (z. B. "Überall abmelden").
Die Widerrufs-Herausforderung
Da JWTs in sich geschlossen sind, kannst du einen Token vor seinem Ablauf ohne zusätzliche Infrastruktur nicht ungültig machen:
- Kurze Ablaufzeit + Refresh Tokens: Access Tokens laufen schnell ab; Refresh Tokens in der DB widerrufen.
- Token-Sperrliste: Widerrufene
jti-Werte in Redis speichern (bringt etwas Zustandsbehaftung zurück). - Geheimnis-Rotation: Signiergeheimnis rotieren, um alle Tokens ungültig zu machen (betrifft alle Benutzer).
OAuth 2.0 und OpenID Connect
OAuth 2.0
OAuth 2.0 ist ein Autorisierungsframework, das kein spezifisches Token-Format vorschreibt. JWTs werden jedoch häufig als OAuth-Access-Tokens verwendet, weil sie in sich geschlossen sind und Autorisierungs-Scopes tragen können:
{
"iss": "https://auth.example.com",
"sub": "user_123",
"aud": "https://api.example.com",
"scope": "read:profile write:posts",
"exp": 1893456000
}
OpenID Connect (OIDC)
OpenID Connect ist eine Identitätsschicht, die auf OAuth 2.0 aufbaut. Es führt das ID Token ein, das immer ein JWT ist und Identitäts-Claims über den authentifizierten Benutzer enthält:
{
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"aud": "my-client-id.apps.googleusercontent.com",
"email": "[email protected]",
"name": "Jane Doe",
"exp": 1893456000,
"iat": 1893452400
}
OIDC definiert auch den JWKS-Endpunkt (/.well-known/jwks.json), wo der Autorisierungsserver seine öffentlichen Schlüssel veröffentlicht. Jeder Dienst kann diese Schlüssel abrufen, um Tokens ohne vorherigen Schlüsselaustausch zu verifizieren.
Best Practices
- Asymmetrische Algorithmen (RS256/ES256) für öffentliche Systeme verwenden — privaten Signaturschlüssel schützen, während jeder mit dem öffentlichen Schlüssel verifizieren kann.
- Kurze
exp-Werte für Access Tokens festlegen — 15 Minuten ist ein gängiger, sicherer Standardwert. - Refresh Tokens immer rotieren — bei jeder Verwendung neuen Token ausstellen und alten ungültig machen.
- Alle relevanten Claims validieren — mindestens
iss,aud,expundnbfprüfen. - Erwarteten Algorithmus explizit angeben — nie allein auf das
alg-Feld im Token-Header verlassen. - Keine sensiblen Daten im Payload speichern — er ist kodiert, nicht verschlüsselt.
- Immer HTTPS verwenden — Tokens niemals über unverschlüsselte Verbindungen übertragen.
- Tokens sicher speichern —
HttpOnly,Secure,SameSite=Strict-Cookies gegenüber localStorage bevorzugen. - Token-Widerrufsstrategie implementieren — Sperrliste oder kurzlebige Tokens mit Refresh-Token-Rotation verwenden.
- JWT-Bibliotheken aktuell halten — Sicherheitslücken werden in neueren Versionen behoben.
Häufig gestellte Fragen (FAQ)
F: Kann ich einen JWT-Payload entschlüsseln? A: JWT-Payloads sind Base64url-kodiert, nicht verschlüsselt — kein Schlüssel wird benötigt, um sie zu lesen. Wenn du Vertraulichkeit benötigst, verwende JWE (JSON Web Encryption, RFC 7516), das den Payload tatsächlich verschlüsselt.
F: Was ist der Unterschied zwischen decode und verify?
A: decode dekodiert die Teile nur Base64url und gibt das JSON ohne Sicherheitsprüfungen zurück. verify prüft zusätzlich die Signatur gegen deinen Schlüssel und validiert zeitbasierte Claims wie exp. Verwende in der Produktion immer verify.
F: Wie handhabe ich die Token-Erneuerung sicher? A: Stelle ein kurzlebiges Access Token (15–60 Min.) und ein langlebiges Refresh Token (Tage bis Wochen) aus. Speichere Refresh Tokens serverseitig, damit sie widerrufen werden können. Wenn das Access Token abläuft, sendet der Client das Refresh Token an einen dedizierten Endpunkt, um ein neues Access Token zu erhalten.
F: Kann ich JWT für Authentifizierung und Autorisierung verwenden?
A: Ja. Füge Identitäts-Claims (wer der Benutzer ist) und Autorisierungs-Claims (was er tun darf, z. B. roles, scope) in denselben Token ein. Halte die Payload-Größe angemessen.
F: Was passiert, wenn mein JWT-Geheimnis durchsickert? A: Ein Angreifer kann gültige Tokens für jeden Benutzer fälschen. Rotiere das Geheimnis sofort (dies macht alle vorhandenen Tokens ungültig), untersuche die Ursache des Lecks und erzwinge die erneute Anmeldung aller Benutzer.
F: Sollte ich HS256 oder RS256 verwenden? A: Verwende RS256 (oder ES256), wenn mehrere Dienste Tokens verifizieren oder wenn dem Verifizierer nicht vollständig vertraut wird. Verwende HS256 nur, wenn derselbe Dienst Tokens sowohl ausstellt als auch verifiziert und das Geheimnis streng kontrolliert werden kann.
F: Wie groß kann ein JWT werden? A: Halte JWTs unter 4 KB, um HTTP-Header-Größenbeschränkungen und Leistungsprobleme zu vermeiden. Bette keine großen Datenstrukturen im Payload ein; referenziere sie stattdessen per ID.