JWT erklärt: Aufbau, Sicherheit und Best Practices
Was ist ein JWT, welche Algorithmen gibt es und wie nutzt man sie sicher? Inklusive der häufigsten Pitfalls.
JSON Web Tokens (JWT) sind heute der De-facto-Standard für stateless Authentifizierung in APIs. Sie sind elegant, kompakt und – falsch eingesetzt – ein hervorragendes Sicherheitsrisiko. Dieser Artikel erklärt Aufbau, Algorithmen und die wichtigsten Pitfalls.
Was ist ein JWT?
Ein JWT ist ein String mit drei Teilen, getrennt durch Punkte: Header.Payload.Signature. Jeder Teil ist Base64URL-kodiert (wie Base64, aber ohne Padding und mit URL-sicheren Zeichen).
Wichtig: JWTs sind signiert, nicht verschlüsselt. Jeder kann den Payload lesen, indem er ihn Base64-decodiert. Speichere also keine Passwörter oder andere Geheimnisse darin.
Beispiel-Token zerlegt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNzMwMDAwMDAwLCJleHAiOjE3MzAwMDM2MDB9
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cHeader (decodiert):
{
"alg": "HS256",
"typ": "JWT"
}Payload (decodiert):
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1730000000,
"exp": 1730003600
}Die Signatur entsteht aus HMAC_SHA256(base64url(header) + "." + base64url(payload), secret). Der Server prüft beim Empfang nur diese Signatur – ist sie gültig, ist der Token nicht manipuliert.
Signatur-Algorithmen: HS256 vs RS256 vs ES256
- HS256 (HMAC-SHA256) – Symmetrisch. Das gleiche Geheimnis signiert und verifiziert. Schnell und einfach, aber jeder, der verifizieren kann, kann auch signieren. Geeignet, wenn ein einziger Service beides macht.
- RS256 (RSA-SHA256) – Asymmetrisch. Privater Key signiert, öffentlicher Key verifiziert. Ideal, wenn mehrere Services Tokens prüfen sollen, ohne signieren zu dürfen. Standard bei OIDC/OAuth2.
- ES256 (ECDSA P-256) – Wie RS256, aber mit Elliptic Curves. Kürzere Signaturen (~64 Byte statt ~256), schneller zu verifizieren. Heute die empfohlene Wahl für neue Systeme.
Häufige Claims
- iss (issuer) – wer den Token ausgestellt hat
- sub (subject) – über wen der Token ist (User-ID)
- aud (audience) – für wen der Token bestimmt ist
- exp (expiration) – Unix-Timestamp, ab wann ungültig
- iat (issued at) – wann ausgestellt
- nbf (not before) – ab wann gültig
- jti (JWT ID) – eindeutige ID, z.B. für Revocation-Listen
Mindestens exp solltest Du immer setzen. Ohne exp ist ein gestohlener Token unbegrenzt gültig.
Sicherheits-Pitfalls
1. alg: none
Frühere Bibliotheken akzeptierten Tokens mit {"alg":"none"} ohne Signatur. Wenn Deine Library das noch tut: sofort patchen. Akzeptiere serverseitig immer nur einen festen Algorithmus, niemals den aus dem Header.
2. Algorithm-Confusion
Wenn der Server RS256 erwartet aber den Algorithmus aus dem Header übernimmt, kann ein Angreifer HS256 mit dem öffentlichen RSA-Key als Geheimnis signieren – und der Server akzeptiert es. Lösung: Algorithmus hart codieren.
3. Kein exp
Tokens ohne Ablauf sind ein Sicherheits-GAU. Standard sollte 15 Minuten bis 1 Stunde sein.
4. JWT in LocalStorage
Im LocalStorage abgelegte Tokens sind über jeden XSS-Bug abgreifbar. Besser: HttpOnly-Cookie mit Secure und SameSite=Lax oder Strict. Achte aber auf CSRF-Schutz.
5. Schwacher HMAC-Schlüssel
Mindestens 256 Bit (32 Byte) Zufall, niemals ein Passwort oder "secret". Tools wie openssl rand -base64 32 erledigen das in einer Zeile.
Best Practices
- Kurze Lifetime für Access-Tokens (5–60 Minuten).
- Lange Lifetime nur für Refresh-Tokens, und diese rotieren bei jeder Nutzung.
- Algorithmus hart codieren – nie aus dem Header übernehmen.
- HttpOnly-Cookie statt LocalStorage, oder Memory-only im SPA.
- JTI + Server-seitige Blacklist, wenn Du Revocation brauchst (sonst gilt der Token bis
exp). - Niemals sensible Daten im Payload – er ist nur kodiert, nicht verschlüsselt.