JSON Web Tokens (JWTs) sind für die API-Authentifizierung und Single Sign-on weit verbreitet. Das Format ist einfach, überlässt aber viele Sicherheitsentscheidungen den Entwicklern und der Bibliothek. RFC 8725, die JWT Best Current Practices, sammelt die bekannten Fehler und zeigt, wie man sie vermeidet; dieser Artikel geht die Punkte durch, die wir als Erstes prüfen würden.
Ein JWT ist signiert, nicht geheim
Ein signiertes JWT nach RFC 7519 besteht aus drei durch Punkte getrennten Teilen: einem Header, einem Payload mit Claims und einer Signatur. Die ersten beiden sind nur Base64url-kodiert. Jeder, der das Token besitzt, kann sie lesen. Hier ein dekodiertes Beispiel:
{
"alg": "RS256",
"typ": "at+jwt",
"kid": "2026-10-key-1"
}
{
"iss": "https://auth.example.eu",
"sub": "user-4821",
"aud": "api.example.eu",
"iat": 1792140000,
"exp": 1792140900,
"role": "editor"
}
Die Signatur beweist, dass die Claims nicht verändert wurden. Sie verbirgt sie nicht. RFC 7519 verlangt, dass bei Tokens mit datenschutzrelevanten Informationen Maßnahmen gegen deren Offenlegung getroffen werden, etwa durch ein verschlüsseltes JWT (JWE). In der Praxis heißt das: Halten Sie personenbezogene Daten, interne Kennungen, die Sie nicht veröffentlichen würden, und alles Geheime aus dem Payload heraus.
Falle 1: "alg": "none" akzeptieren
RFC 7519 erlaubt „ungesicherte“ JWTs mit "alg": "none" und leerer Signatur. RFC 8725 beschreibt Bibliotheken, die solche Tokens akzeptierten und die Signaturprüfung komplett übersprangen. Ein Angreifer konnte dann beliebige Claims hineinschreiben.
RFC 7518 legt fest, dass Implementierungen ungesicherte Tokens standardmäßig nicht akzeptieren dürfen, und das OWASP REST Security Cheat Sheet rät, sie überhaupt nicht zuzulassen. Prüfen Sie, ob Ihre Bibliothek none ablehnt, und testen Sie das mit einem präparierten Token.
Falle 2: das Token den Algorithmus wählen lassen
Den Wert alg im Header bestimmt, wer das Token erstellt hat – und das kann ein Angreifer sein. RFC 8725 beschreibt den klassischen Verwechslungsangriff: Ein Server erwartet RS256, verifiziert mit einem öffentlichen RSA-Schlüssel. Der Angreifer ändert alg auf HS256 und signiert das Token per HMAC, mit diesem öffentlichen Schlüssel als Secret. Eine Bibliothek, die dem Header folgt, verifiziert es erfolgreich – und der öffentliche Schlüssel ist per Definition öffentlich.
Die Lösung: Legen Sie den Algorithmus auf dem Server fest. Laut OWASP darf sich die Verifizierung bei der Wahl des Algorithmus nicht auf den JWT-Header stützen. RFC 8725 ergänzt, dass jeder Schlüssel mit genau einem Algorithmus verwendet werden sollte. Im Code sieht das etwa so aus:
verify(token,
key = issuer_public_key,
algorithms = ["RS256"],
issuer = "https://auth.example.eu",
audience = "api.example.eu")
Falle 3: schwache HMAC-Secrets
Bei HS256 kann jeder, der ein Token abfängt, offline versuchen, das Secret zu erraten, so schnell es seine Hardware erlaubt. RFC 8725 stellt klar, dass Passwörter, die sich Menschen merken können, nicht direkt als HMAC-Schlüssel verwendet werden dürfen. RFC 7518 verlangt einen Schlüssel, der mindestens so lang ist wie die Hash-Ausgabe, bei HS256 also 256 Bit.
openssl rand -base64 32
Das liefert 32 zufällige Bytes, also 256 Bit. Bewahren Sie sie in einem Secrets-Manager auf, nicht im Repository.
Gemeinsame Secrets haben eine zweite Schwäche. Jeder Dienst, der ein HMAC-Token verifizieren kann, kann auch eines erstellen. OWASP bevorzugt aus diesem Grund Signaturen gegenüber MACs: Wenn mehrere Dienste Tokens validieren, könnte sonst ein einziger kompromittierter Dienst Tokens für alle anderen ausstellen. Mit asymmetrischen Algorithmen wie RS256 oder ES256 geben Sie nur den öffentlichen Schlüssel weiter.
Falle 4: Claims nicht prüfen
Eine gültige Signatur beweist nur, wer das Token ausgestellt hat. Die Claims entscheiden, ob dieser Dienst es jetzt akzeptieren sollte.
exp– laut RFC 7519 darf das Token ab diesem Zeitpunkt nicht mehr akzeptiert werden. Ein kleiner Spielraum für abweichende Uhren, meist höchstens wenige Minuten, ist zulässig.nbf– falls vorhanden, lehnen Sie das Token vor diesem Zeitpunkt ab.iss– prüfen Sie, ob der Aussteller derjenige ist, dem Sie vertrauen, und ob ihm der Signaturschlüssel gehört.aud– laut RFC 7519 muss das Token abgelehnt werden, wenn sich der Dienst nicht in der Zielgruppe wiederfindet.
OWASP empfiehlt, iss, aud und exp bei API-Access-Tokens standardmäßig zu verlangen und zu prüfen. RFC 8725 erklärt, warum die Zielgruppe wichtig ist: Ohne sie kann ein Token, das für einen Dienst ausgestellt wurde, bei einem anderen Dienst wiederverwendet werden, der demselben Aussteller vertraut. Es empfiehlt außerdem eine explizite Typisierung mit dem Header typ, damit sich eine Art von Token nicht dort verwenden lässt, wo eine andere erwartet wird. Für OAuth-Access-Tokens verwendet RFC 9068 den Wert at+jwt, wie im Beispiel oben.
Falle 5: kid, jku und x5u vertrauen
Header-Parameter, die dem Server sagen, wo er den Schlüssel findet, kontrolliert ebenfalls der Angreifer. RFC 8725 nennt die Risiken:
kid(Key ID) kann bei einer Abfrage in Datenbank oder Verzeichnisdienst SQL- oder LDAP-Injection ermöglichen. Dasselbe gilt, wenn daraus ein Dateipfad gebildet wird.jkuundx5uverweisen auf eine URL, von der Schlüssel abgerufen werden. Wer jeder URL folgt, ermöglicht Server-Side Request Forgery, und ein Angreifer könnte auf seinen eigenen Schlüssel verweisen.
Behandeln Sie kid als undurchsichtige Kennung und schlagen Sie sie in einer festen Liste von Schlüsseln nach, die Sie kontrollieren. Rufen Sie Schlüssel nur von URLs auf einer Allowlist ab; RFC 8725 ergänzt, dass bei diesen Anfragen keine Cookies mitgesendet werden sollten.
Falle 6: langlebige Tokens ohne Widerruf
Ein JWT bleibt gültig, bis es abläuft – auch nachdem sich der Benutzer abgemeldet oder ein Administrator das Konto gesperrt hat. OWASP weist darauf hin, dass Tokens die Sitzung überdauern können, die sie repräsentieren.
- Halten Sie Access Tokens kurzlebig, gemessen in Minuten statt Tagen.
- Verwenden Sie Refresh Tokens, die der Server bei jeder Nutzung prüft und widerrufen kann.
- Führen Sie für Abmeldungen und Kontosperrungen eine Sperrliste der Token-IDs (
jti), bis diese Tokens ablaufen, wie OWASP vorschlägt. - Wechseln Sie Signaturschlüssel nach Zeitplan und halten Sie einen getesteten Prozess für einen Notfallwechsel bereit.
Falle 7: Tokens dort speichern, wo Skripte sie lesen können
Tokens in localStorage kann jedes JavaScript auf der Seite lesen. Das OWASP HTML5 Security Cheat Sheet warnt, dass eine einzige Cross-Site-Scripting-Lücke alles aus diesen Speicherobjekten stehlen kann, und rät davon ab, Sitzungskennungen dort abzulegen.
Für Browser-Anwendungen hält ein Cookie mit HttpOnly, Secure und passender SameSite-Einstellung das Token von Skripten fern. Mit Cookies wird Cross-Site Request Forgery relevant; planen Sie also auch dafür. Was immer Sie wählen: Eine starke Content Security Policy verringert von vornherein die Gefahr von XSS.
Checkliste für die Validierung
- Legen Sie die akzeptierten Algorithmen auf dem Server fest und lehnen Sie
noneab. - Verwenden Sie eine Bibliothek, die die Signatur verifiziert, bevor Sie Claims lesen.
- Prüfen Sie
exp,nbf,issundaudbei jeder Anfrage. - Trennen Sie Token-Typen mit
typoder unterschiedlichen Zielgruppen. - Schlagen Sie Schlüssel per
kidin einem festen Satz nach; rufen Sie nie Schlüssel von beliebigen URLs ab. - Verwenden Sie zufällige HMAC-Schlüssel mit mindestens 256 Bit oder asymmetrische Schlüssel.
- Halten Sie Payloads frei von Secrets und unnötigen personenbezogenen Daten.
- Halten Sie Access Tokens kurzlebig und unterstützen Sie den Widerruf.
- Speichern Sie Tokens im Browser außerhalb der Reichweite von JavaScript.
Wo Sie anfangen
Fügen Sie ein Token aus Ihrer Testumgebung in unseren JWT-Decoder ein, um genau zu sehen, was es preisgibt, und gleichen Sie seine Werte für alg, aud und exp mit dieser Liste ab. Fügen Sie keine produktiven Tokens in Online-Tools ein.
Wenn Sie eine zweite Meinung zu Ihrem Authentifizierungskonzept oder ein Code-Review Ihrer Token-Verarbeitung wünschen, unterstützt Sie unser Team für sichere Softwareentwicklung.
