Fast jeder Login, den Sie heute nutzen, endet mit einem JSON Web Token. Der Identity Provider gibt Ihrer App eines, die App schickt es bei jedem API-Aufruf mit, und die API vertraut ihm, ohne jemanden zu fragen. Genau das ist der Sinn eines JWT, und genau deshalb wird aus einem kleinen Fehler bei der Prüfung schnell „jeder kann Admin sein”.
Dieser Leitfaden erklärt, was ein JWT ist, Teil für Teil, wie ein Server es prüfen sollte, welcher Signaturalgorithmus passt und welche Angriffe auf reale Systeme heute noch funktionieren. Jedes Beispiel-Token lässt sich in den kostenlosen JWT-Decoder mit Security-Analyse einfügen, der vollständig im Browser läuft.

Was ist ein JWT? Ein JSON Web Token als Beispiel
Ein JWT (JSON Web Token, ausgesprochen wie das englische „jot”) ist ein kompakter, URL-sicherer String, der eine Menge von Claims und eine Signatur darüber trägt. Definiert ist es in RFC 7519; die Formate für Signatur und Verschlüsselung darunter heißen JWS (RFC 7515) und JWE (RFC 7516).
Ein signiertes JWT hat drei Teile, getrennt durch Punkte:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzQyIiwicm9sZSI6ImVkaXRvciIsImlhdCI6MTc2NzIyNTYwMCwiZXhwIjoxNzY3MjI2NTAwfQ.92XVsnsq7eHmUoHn1v4cCfyufbIv5l0Pm8L_Ec6PDH8Dekodiert man die ersten beiden Teile aus Base64url, erhält man zwei gewöhnliche JSON-Objekte:
{ "alg": "HS256", "typ": "JWT" }{ "sub": "user_42", "role": "editor", "iat": 1767225600, "exp": 1767226500 }Der dritte Teil ist die Signatur: 32 Byte HMAC-SHA256 über header.payload, berechnet mit einem Secret, das nur der Aussteller kennt. Ändert man ein Zeichen der Payload, etwa "role": "admin", passt die Signatur nicht mehr.
Mehr ist ein JWT nicht: Claims plus Beweis, wer sie geschrieben hat. Spannend ist alles drumherum.
Aufbau eines JSON Web Tokens

Der Header
Der Header sagt, wie das Token zu prüfen ist. Die wichtigen Felder:
| Feld | Bedeutung | Vorsicht bei |
|---|---|---|
alg |
Signaturalgorithmus: HS256, RS256, ES256, EdDSA … |
none oder einem Algorithmus, den Ihr Server nie ausstellt |
typ |
Tokentyp: JWT oder at+jwt für OAuth-Access-Tokens (RFC 9068) |
Ohne Typ werden Token-Verwechslungen leichter |
kid |
Key ID: welcher Schlüssel aus dem Key Set signiert hat | Verwendung als Dateipfad oder SQL-Wert |
jku, x5u |
URL eines Key Sets oder Zertifikats | Wird vom Verifier blind abgerufen |
jwk |
Öffentlicher Schlüssel direkt im Token | Wird als Prüfschlüssel vertraut |
Die Payload: Claims
Die Payload ist ein JSON-Objekt aus Claims. RFC 7519 registriert sieben davon; alles andere legt der Aussteller fest.
| Claim | Name | Was der Verifier damit tut |
|---|---|---|
iss |
Issuer (Aussteller) | Muss zum vertrauenswürdigen Identity Provider passen |
sub |
Subject | Der Nutzer oder Client, um den es geht |
aud |
Audience (Zielgruppe) | Muss die Kennung Ihrer API enthalten |
exp |
Expiration Time | Nach diesem Zeitpunkt ablehnen |
nbf |
Not Before | Vor diesem Zeitpunkt ablehnen |
iat |
Issued At | Altersprüfung, Debugging |
jti |
JWT ID | Eindeutige ID für Einmalnutzung oder Widerruf |
Zeiten sind NumericDate: ganze Sekunden seit 1970-01-01 UTC. Erstaunlich viele Fehler entstehen, weil Millisekunden geschrieben werden (Date.now() in JavaScript): Das Token läuft dann erst in Zehntausenden Jahren ab.
Die Signatur
Bei HS256 lautet die Signatur:
HMAC-SHA256( base64url(header) + "." + base64url(payload), secret )Bei RS256, ES256 oder EdDSA ist es eine Public-Key-Signatur über dieselbe Eingabe. In beiden Fällen deckt sie den kodierten Header und die Payload ab, der Server prüft also genau die Bytes, die er erhalten hat.
Signiert ist nicht verschlüsselt
Base64url ist eine Kodierung, keine Verschlüsselung. Wer ein JWT hat, liest jeden Claim, ganz ohne Schlüssel:
node -e 'console.log(JSON.parse(Buffer.from(process.argv[1].split(".")[1], "base64url")))' "$TOKEN"Das ist das häufigste Missverständnis rund um JWTs, und es hat Folgen. Tokens landen im Browser-Speicher, in Logs, Analytics, Crash-Reports und Proxy-Caches. Was in der Payload steht, veröffentlichen Sie an alle diese Orte.
Ein JWT ist eine Bordkarte. Wer sie sieht, liest den Namen darauf, wer sie hält, steigt ein, und nur der Aussteller kann das Siegel drucken, das sie gültig macht.
Die Payload also auf Kennungen und Berechtigungen beschränken. Keine Passwörter, API-Keys, Kartennummern und keine personenbezogenen Daten über das hinaus, was die API braucht. Muss der Inhalt wirklich vertraulich sein, ein verschlüsseltes JWT (JWE, fünf statt drei Teile) verwenden oder die Daten auf dem Server halten und eine undurchsichtige ID ins Token schreiben.
Wie man ein JWT richtig prüft
Dekodieren zeigt, was ein Token behauptet. Prüfen entscheidet, ob man es glaubt. Die Prüfung ist eine Checkliste, und die Reihenfolge zählt: Jeder Schritt schließt einen bestimmten Angriff.

- Eingabe begrenzen. Übergroße Tokens vor dem Parsen ablehnen.
- Algorithmus festlegen. Nur die Algorithmen akzeptieren, die Sie tatsächlich ausstellen, konfiguriert auf dem Server. Nie das
algdes Tokens entscheiden lassen. - Den Schlüssel selbst wählen. Die
kidim eigenen Key Set nachschlagen (ein JWKS Ihres Identity Providers).jwk,jkuundx5uaus dem Token ignorieren. - Signatur prüfen.
- Zeit prüfen:
expundnbfmit kleiner Toleranz für Uhrabweichungen (30–60 Sekunden), undexpals Pflichtfeld verlangen. - Kontext prüfen:
issist Ihr Identity Provider,audenthält Ihre API,typist das, was dieser Endpunkt erwartet.
Mit einer gepflegten Bibliothek sind das wenige Zeilen. In Node.js mit jose:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://auth.example.com/.well-known/jwks.json'));
const { payload } = await jwtVerify(token, jwks, { algorithms: ['ES256'], issuer: 'https://auth.example.com', audience: 'https://api.example.com', typ: 'at+jwt', clockTolerance: 30,});Und in Python mit PyJWT:
import jwt
jwks = jwt.PyJWKClient("https://auth.example.com/.well-known/jwks.json")key = jwks.get_signing_key_from_jwt(token).key
claims = jwt.decode( token, key, algorithms=["ES256"], issuer="https://auth.example.com", audience="https://api.example.com", leeway=30, options={"require": ["exp", "iat", "iss", "aud", "sub"]},)Die hervorgehobenen Zeilen sind die Sicherheit. Streicht man eine davon, „funktioniert” der Code in jedem Test weiter, und genau deshalb werden sie gestrichen.
HS256 vs. RS256 vs. ES256: welcher Algorithmus?
Die eigentliche Frage ist nicht Geschwindigkeit oder Schlüssellänge, sondern wer Tokens ausstellen kann.

| Algorithmus | Typ | Signaturgröße | Einsetzen, wenn |
|---|---|---|---|
| HS256 | HMAC, gemeinsames Secret | 32 Byte | Ein Dienst seine eigenen Tokens ausstellt und prüft |
| RS256 | RSA PKCS#1 v1.5 | 256 Byte (2048-Bit-Schlüssel) | Breiteste Unterstützung; Standard der meisten Identity Provider |
| PS256 | RSA-PSS | 256 Byte (2048-Bit-Schlüssel) | RSA mit dem moderneren Padding |
| ES256 | ECDSA P-256 | 64 Byte | Kleine Tokens, breite Unterstützung; guter Standard für neue Systeme |
| EdDSA | Ed25519 | 64 Byte | Einfachste sichere Signaturen, wo Ihre Bibliotheken es unterstützen |
Bei HS256 ist der Prüfschlüssel der Signaturschlüssel. Prüfen fünf Microservices Tokens, halten fünf Dienste ein Secret, mit dem sie für jeden beliebigen Nutzer ein Token erzeugen können, und ein Leck im am schlechtesten geschützten Dienst kompromittiert alle. Mit einem asymmetrischen Verfahren hält nur der Identity Provider den privaten Schlüssel; Dienste holen die öffentlichen Schlüssel von einer JWKS-URL, und Schlüsselrotation bedeutet einfach eine neue kid.
Wer HS256 nutzt, braucht ein zufälliges, langes Secret: RFC 7518 verlangt einen Schlüssel mindestens so lang wie die Hash-Ausgabe (256 Bit bei HS256), und RFC 8725 schließt merkbare Passwörter als HMAC-Schlüssel aus.
openssl rand -base64 32Sechs JWT-Angriffe, die heute noch funktionieren
Keiner davon bricht HMAC, RSA oder ECDSA. Sie nutzen den Verifier aus: Code, der dem Token glaubt, wie es geprüft werden will. Tim McLeans Analyse kritischer Schwachstellen in JWT-Bibliotheken von 2015 hat die ersten beiden berühmt gemacht, und sie tauchen in Eigenbau-Code und alten Abhängigkeiten immer noch auf.
1. alg: none
JWS definiert einen „ungesicherten” Algorithmus namens none mit leerer Signatur. Ein Angreifer nimmt ein gültiges Token, ändert die Payload, setzt "alg": "none", entfernt die Signatur und lässt den letzten Punkt stehen. Ein Verifier, der dem Header folgt, akzeptiert es. Varianten wie None oder nOnE rutschen an naiven Stringvergleichen vorbei.
Fix: eine explizite Allow-List der Algorithmen auf dem Server, wie im Code oben.
2. Algorithm Confusion (RS256 zu HS256)
Der Server erwartet RS256 und prüft mit dem öffentlichen Schlüssel des Ausstellers. Der Angreifer ändert alg auf HS256 und signiert das Token per HMAC mit genau diesem öffentlichen Schlüssel als Secret, der ja öffentlich ist. Eine Bibliothek, die den Algorithmus aus dem Header nimmt, rechnet nun HMAC mit denselben Schlüssel-Bytes, und das gefälschte Token ist gültig.
Fix: den Algorithmus festlegen und Bibliotheken nutzen, die einen Schlüssel an genau einen Algorithmustyp binden.
3. Schwache HMAC-Secrets
Ein HS256-Token ist ein Offline-Rätsel: Wer ein Token hat, kann Kandidaten-Secrets testen, ohne Ihren Server zu berühren. secret, changeme, der Beispielwert des Frameworks oder your-256-bit-secret aus einem Tutorial fallen in Sekunden; hashcat hat sogar einen eigenen Modus für JWTs (16500).
Fix: ein zufälliges 256-Bit-Secret aus einem Secret Manager oder der Wechsel zu einem asymmetrischen Verfahren.
4. Schlüssel-Injection über den Header
jwk bettet einen öffentlichen Schlüssel ins Token ein, jku oder x5u zeigen auf eine URL mit dem Schlüssel. Ein Verifier, der sie nutzt, lässt den Angreifer mit dem eigenen Schlüssel signieren und den passenden öffentlichen Schlüssel gleich mitliefern. kid ist subtiler: Es ist nur ein String, und Code, der ihn als Dateipfad (../../dev/null) oder in einer SQL-Abfrage verwendet, macht daraus Path Traversal oder Injection.
Fix: Schlüssel nur aus dem eigenen, konfigurierten Key Set nehmen; kid als undurchsichtigen Lookup-Schlüssel behandeln. RFC 8725 sagt es ausdrücklich: empfangenen Claims nicht vertrauen.
5. Fehlende Prüfung von Audience und Issuer
Ihr Unternehmen hat zwei APIs, die demselben Identity Provider vertrauen. Ein Token für die harmlose API wird gegen die Payment-API wiederverwendet und ist gültig, denn Signatur und Ablauf stimmen. Niemand hat aud geprüft.
Fix: Jede API prüft, dass aud sie selbst nennt und iss der erwartete Aussteller ist. Explizite typ-Werte verhindern, dass ein ID-Token als Access Token durchgeht.
6. Tokens, die ewig leben
Ein Token ohne exp oder mit Monaten Laufzeit ist ein langlebiges Passwort, das sich nicht ändern lässt. Ein gestohlenes funktioniert weiter, nachdem sich der Nutzer abmeldet, sein Passwort ändert oder das Unternehmen verlässt.
Fix: Access Tokens mit Laufzeiten in Minuten, exp als Pflichtfeld im Verifier und rotierende Refresh Tokens, die der Server widerrufen kann.
Auch Bibliotheksfehler kommen vor: 2022 akzeptierte die ECDSA-Implementierung von Java (Versionen 15 bis 18) eine Signatur aus lauter Nullen als gültig für jede Nachricht. Damit war auf ungepatchten Laufzeitumgebungen jedes ES256-Token fälschbar (CVE-2022-21449, „Psychic Signatures”). Krypto-Laufzeiten und JWT-Bibliotheken aktuell zu halten, gehört zur Checkliste.
JWT-Decoder: ein Token lesen, ohne es preiszugeben
Ein JWT ist ein Bearer-Credential: Wer ein gültiges Produktions-Token hat, kann meist Ihre API im Namen dieses Nutzers aufrufen. Es in irgendeine Website einzufügen, die es an einen Server hochlädt, heißt, es zu verschenken.
Drei sichere Wege:
- Kommandozeile: der Node.js-Einzeiler oben oder das Pendant Ihrer Sprache. Nichts verlässt den Rechner.
- Ein lokaler Browser-Decoder: Der JWT-Decoder mit Security-Analyse auf dieser Website dekodiert, prüft und auditiert Tokens mit Web Crypto direkt in der Seite; Tokens, Secrets und Schlüssel werden nie hochgeladen, gespeichert oder in die URL geschrieben.
- Abgelaufene oder Test-Tokens, wenn Sie nur die Struktur sehen wollen.
So sieht der Bericht für ein Token aus, das mit your-256-bit-secret signiert wurde, dem Beispiel-Secret aus unzähligen Tutorials:

Zuerst meldet er die fälschbare Signatur, dann die mehrjährige Laufzeit und die fehlenden iss und aud, jeweils mit Fix. Fügen Sie den öffentlichen Schlüssel oder das JWKS des Ausstellers ein, prüft er auch RS256-, PS256-, ES256- und EdDSA-Signaturen. Für KI-Agenten steht dieselbe Analyse als WebMCP-Tool analyze_jwt bereit.
JWT oder Session-Cookie: wann ein JWT passt
JWTs sind so populär, dass sie auch dort landen, wo eine Session einfacher wäre. Ein ehrlicher Vergleich:
| Serverseitige Session | JWT | |
|---|---|---|
| Zustand | Auf dem Server, Cookie enthält eine ID | In sich geschlossen, mit Schlüssel geprüft |
| Widerruf | Sofort: Session löschen | Wartet auf exp, außer man ergänzt Serverzustand |
| Skalierung | Braucht gemeinsamen Session-Store | Jeder Dienst mit dem öffentlichen Schlüssel kann prüfen |
| Über Dienste und Domains | Umständlich | Genau dafür gebaut |
| Größe pro Request | Ein paar Dutzend Byte | Hunderte Byte bis mehrere KB |
Eine Session für die klassische Web-App mit einem Backend. JWTs, wenn mehrere Dienste, ein API-Gateway oder Dritte die Identität ohne gemeinsamen Store prüfen müssen, und überall dort, wo OAuth 2.0 und OpenID Connect ohnehin eines liefern: ID-Tokens sind immer JWTs, viele Access Tokens ebenfalls.
JWT vs. OAuth
Das sind keine Alternativen. OAuth 2.0 ist ein Protokoll, um im Namen eines Nutzers ein Access Token zu bekommen; ein JWT ist ein Tokenformat. Ein OAuth-Access-Token kann ein JWT sein (RFC 9068 standardisiert dieses Profil mit typ: at+jwt) oder ein undurchsichtiger Zufallsstring, den die API beim Authorization Server prüft. OpenID Connect, die Identitätsschicht über OAuth, nutzt für das ID-Token immer ein JWT.
Beides lässt sich gut kombinieren. In Browser-Apps gehört ein JWT in ein HttpOnly-Cookie, nicht in den localStorage; Details inklusive CSRF-Schutz stehen in JWT nicht in LocalStorage speichern. Wer den Identity Provider selbst betreibt, lässt Tokens von Keycloak oder Authentik ausstellen und signieren, und ein stärkerer Login davor, etwa mit Passkeys, schützt den Moment, in dem ein Token entsteht.
Das HMAC hinter HS256 ist dasselbe Primitiv, mit dem Webhooks signiert werden; Webhook-Signaturen unterliegen denselben Regeln für Secret-Stärke und zeitkonstanten Vergleich.
Fazit
Ein JWT ist ein lesbares JSON-Dokument mit Signatur. Das Format ist einfach und die Kryptografie solide; in der Praxis scheitert der Code drumherum. Algorithmus festlegen, Schlüssel selbst wählen, exp verlangen, iss und aud prüfen, Tokens kurzlebig halten und Secrets aus der Payload heraushalten. Mit einer gepflegten Bibliothek umgesetzt, ist ein JWT eines der bequemsten Credentials, die man ausgeben kann.
Und wenn Sie das nächste Mal sehen wollen, was in einem steckt: lokal dekodieren, nicht in einer Seite, die es hochlädt.






Aus der Community
Diskussion auf Mastodon & Bluesky
Antworten auf diesen Artikel aus dem offenen Netz, live geladen. Keine Werbung, keine Tracking-Skripte.
Antworte dort — es erscheint hier automatisch.