Skip to content

Guide

JWT-Tokens: Dekodieren, verifizieren und häufige Fehler vermeiden

Drei Base64url-Segmente, eine Signatur, die Sie tatsächlich verifizieren müssen, und ein historischer Bug – alg: none –, der noch immer in manchen Bibliotheken steckt.

By Published

Ein JWT besteht aus drei durch Punkte verbundenen, Base64url-codierten Zeichenketten. Die erste sind Metadaten, die zweite sind die Daten, um die es Ihnen eigentlich geht, und die dritte ist die Signatur, die beweist, dass die ersten beiden nicht verändert wurden. Die meisten JWT-Bugs entstehen, weil der dritte Teil als optional behandelt wird — oder weil falsch verstanden wird, was das Format überhaupt garantiert.

Die dreiteilige Struktur

Ein JWT sieht so aus:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNzQ4NjQ4MDAwfQ.qZdfL_HxR_eRT3z3qZX7Rqv0kK7r0sQYMfRBlLcM2hI

Trennt man an den Punkten, erhält man Header, Payload und Signatur. Jeder der ersten beiden ist ein JSON-Objekt, das Base64url-codiertwurde — eine URL-sichere Variante von Base64, die + durch -, / durch _ ersetzt und das Padding weglässt. Fügen Sie ein beliebiges Token in unseren JWT-Decoder ein, um die drei Teile als reines JSON ausgerollt zu sehen.

Der Header

{
  "alg": "HS256",
  "typ": "JWT"
}

Zwei Felder. alg benennt den Signaturalgorithmus; die Werte behandeln wir unten. typkennzeichnet das Token als JWT — manche Tools verlangen es, die meisten ignorieren es. Einige Tokens enthalten zusätzlich kid (Key ID), damit der Prüfer weiß, welchen öffentlichen Schlüssel aus einem Schlüsselsatz er nutzen soll.

Der Payload

{
  "sub": "1234567890",
  "iat": 1748648000,
  "exp": 1748651600,
  "iss": "https://auth.example.com",
  "aud": "https://api.example.com"
}

Welches JSON-Objekt auch immer Sie wählen. Die Felder heißen Claims. RFC 7519 definiert sieben „registrierte“ Claim-Namen, die Sie wiederverwenden sollten, wenn ihre Bedeutung zu Ihrer passt.

Die Signatur

Die Signatur wird über base64url(header) + „.“ + base64url(payload) mit dem im Header angegebenen Algorithmus und Schlüssel berechnet. Die Signatur selbst ist das dritte Base64url-codierte Segment. Sie ist es, die ein JWT zu einem JWT macht statt zu einer bloßen Base64-Hülle.

Die registrierten Claims

  • iss (Issuer). Wer das Token ausgestellt hat. Meist eine URL, die Ihren Auth-Dienst identifiziert. Prüfer müssen kontrollieren, dass dies dem erwarteten Issuer entspricht.
  • sub (Subject).Worauf sich das Token bezieht — typischerweise die Nutzer-ID. Sollte innerhalb des Issuers eindeutig sein.
  • aud (Audience).Für wen das Token bestimmt ist — die API-URL oder der Dienstname. Ein Prüfer, der nicht der erwarteten Audience entspricht, muss das Token ablehnen, sonst kann ein für einen Dienst bestimmtes Token gegen einen anderen wiederverwendet werden.
  • exp (Expiration). Unix-Zeitstempel (Sekunden seit 1970-01-01 UTC), nach dem das Token ungültig ist. Prüfer müssen abgelaufene Tokens ablehnen.
  • nbf (Not before). Unix-Zeitstempel, vor dem das Token ungültig ist. Optional.
  • iat (Issued at). Unix-Zeitstempel der Ausstellung. Informativ.
  • jti (JWT ID). Eine eindeutige ID für das Token selbst, nützlich für expliziten Widerruf oder zur Verhinderung von Replay-Angriffen.

Darüber hinaus können Sie jedes JSON-serialisierbare Feld in den Payload aufnehmen. Üblich ist es, benutzerdefinierte Claims mit einer URL zu namensräumen (z. B. „https://example.com/roles“), um Kollisionen mit künftigen registrierten Namen zu vermeiden.

HS256 vs. RS256 vs. ES256

HS256 (HMAC-SHA-256, symmetrisch)

Die Signatur ist ein HMAC über den codierten Header und Payload mit einem gemeinsamen Secret. Wer die Signatur verifizieren kann, kann auch eine neue erstellen— derselbe Schlüssel erledigt beide Aufgaben.

Verwenden Sie HS256, wenn Issuer und Prüfer dieselbe Partei sind (gleicher Dienst, gleiches Secret). Verwenden Sie HS256 nicht, wenn Dritte Ihre Tokens verifizieren müssen — Sie müssten das Signatur-Secret teilen, was bedeutet, dass sie Tokens prägen könnten, die wie Ihre aussehen.

RS256 (RSA-SHA-256, asymmetrisch)

Die Signatur ist eine RSA-Signatur mit dem privaten Schlüssel des Issuers. Prüfer nutzen den öffentlichen Schlüssel des Issuers — typischerweise über einen JWKS-Endpunkt veröffentlicht —, um die Signatur zu prüfen. Prüfer können keine Tokens prägen.

Das ist der Standard für föderierte Identität: OpenID Connect, Auth0, Cognito, jeder „Login mit X“-Ablauf. Der Issuer behält den privaten Schlüssel; alle anderen vertrauen dem öffentlichen Schlüssel.

ES256 (ECDSA über P-256, asymmetrisch)

Dasselbe Sicherheitsmodell wie RS256, aber mit Elliptische-Kurven-Signaturen. Die Vorteile: ~10-mal kleinere Signaturgröße (64 Byte statt 256), schnellere Verifizierung und stärkere Sicherheit pro Bit. Der Nachteil: ECDSA-Implementierungen sind leichter falsch zu machen als RSA und haben historisch kritische Bugs hervorgebracht (die „k-Wiederverwendungs“-Katastrophe der PlayStation 3, der CVE-2022-21449-Bug der „psychic signatures“ in Java).

Verwenden Sie ES256 mit einer geprüften Bibliothek (Nodes eingebautes crypto, Gos crypto/ecdsa, Rusts ring, Pythons cryptography). Implementieren Sie ECDSA niemals selbst. Niemals.

EdDSA (Ed25519)

Neuer als ES256, schwerer falsch anzuwenden, die schnellste der asymmetrischen Optionen. In RFC 8037 als JWT-Algorithmus gelistet, aber nicht universell unterstützt — prüfen Sie Ihren Stack vor der Einführung.

Der alg-none-Angriff

Die JWT-Spezifikation definiert einen besonderen Algorithmuswert none, der „unsigniert“ bedeutet. Ein Token mit alg: nonehat ein leeres Signatursegment — die Punkte sind weiterhin da, aber der dritte Teil ist die leere Zeichenkette.

2015 zeigten Forscher, dass mehrere JWT-Bibliotheken das alg-Feld im Header als Verifizierungsanweisung behandelten. Ein Angreifer konnte ein legitimes HS256-Token nehmen, den Header auf alg: noneändern, die Signatur weglassen, und die Bibliothek akzeptierte das veränderte Token als gültig, weil „der Algorithmus sagt, es ist keine Signatur nötig.“

Die Gegenmaßnahme ist, gegen einen erwarteten Algorithmus zu verifizieren, nicht gegen den, den das Token angibt:

// FALSCH — vertraut dem Header
jwt.verify(token, key);

// RICHTIG — fixiert den Algorithmus
jwt.verify(token, key, { algorithms: ["RS256"] });

Moderne Bibliotheken lehnen none entweder standardmäßig ab oder verlangen ein explizites Opt-in. RFC 8725 (JWT Best Current Practices) schreibt das Muster der Algorithmus-Fixierung vor. Prüfen Sie jeden Verifizierungscode, der ihm nicht folgt.

Weitere häufige Fehler

Dem kid-Header ohne Validierung vertrauen

Der kid-Header sagt dem Prüfer, welchen Schlüssel aus einem Schlüsselsatz er nutzen soll. Verwenden Sie kid blind als Dateipfad oder Datenbankschlüssel, kann ein Angreifer einen bösartigen Wert liefern (../../etc/passwd oder SQL-Injection). Behandeln Sie kid stets als opaken Nachschlageschlüssel in einen bekannten Satz, nicht als Pfad oder Query.

Algorithmus-Verwechslung (RS256 → HS256)

Manche Bibliotheken akzeptieren den öffentlichen Schlüssel zur RS256-Verifizierung, aber auch als HMAC-Secret für HS256. Ein Angreifer kann den Header von RS256 auf HS256 ändern, das Token mit dem (öffentlich bekannten) öffentlichen RSA-Schlüssel als HMAC-Secret signieren, und die Bibliothek verifiziert es. Fixieren Sie den Algorithmus.

Langlebige Access-Tokens ohne Widerruf

JWTs sind zustandslos — der Prüfer spricht nicht pro Anfrage mit dem Issuer. Das ist der ganze Performance-Vorteil und das ganze Widerrufsproblem. Sie können ein geleaktes Token vor seinem exp nicht ungültig machen, ohne eine serverseitige Sperrliste zu pflegen, was die Zustandslosigkeit aufhebt.

Das Standardmuster: kurzlebiges Access-Token (5–60 Minuten) plus langlebiges, serverseitig gespeichertes Refresh-Token (Tage bis Wochen). Widerrufen Sie das Refresh-Token, wenn der Nutzer sich abmeldet, wenn ein Gerät als verloren gemeldet wird, wenn ein Admin eine erneute Authentifizierung erzwingt. Das Access-Token läuft kurz darauf von selbst ab.

Geheimnisse in den Payload legen

Der Payload ist Base64url-codiert, nicht verschlüsselt. Jeder mit dem Token kann jeden Claim lesen. Legen Sie keine Passwörter, API-Schlüssel, unter strenge Regulierung fallende personenbezogene Daten oder irgendetwas hinein, das Sie nicht in ein Server-Log schreiben würden. Brauchen Sie Vertraulichkeit, verwenden Sie stattdessen JWE.

Audience- und Issuer-Validierung überspringen

Ein Prüfer, der die Signatur kontrolliert, aber nicht die aud- und iss-Claims, akzeptiert Tokens, die für einen anderen Dienst geprägt wurden. In einer föderierten Umgebung ist das ein kritischer Bug. RFC 8725 schreibt vor, dass beide gegen erwartete Werte validiert werden müssen.

JWTs im localStorage speichern

localStorage ist von jedem JavaScript Ihres Origins lesbar, womit jedes XSS zum vollständigen Token-Diebstahl wird. Bevorzugen Sie HTTP-only-Secure-Cookies für browsergespeicherte Tokens, mit einer CSRF-Strategie obendrauf. Müssen Sie localStorage verwenden, müssen Ihre XSS-Abwehrmaßnahmen wasserdicht sein.

Wann JWT das falsche Werkzeug ist

JWTs lösen ein konkretes Problem: Ein Prüfer muss einem Claim vertrauen, ohne den Issuer zu kontaktieren. Dieses Problem existiert tatsächlich bei föderierter Identität und Microservices. Bei einer eigenen Web-App, in der Client und Server von demselben Team betrieben werden, existiert es meist nicht.

Für diesen Fall ist eine reguläre serverseitige Session mit einem HTTP-only-Secure-Cookie einfacher:

  • Sofort widerrufbar. Löschen Sie die Session-Zeile, und der Nutzer ist abgemeldet.
  • Kleiner bei der Übertragung. Eine Session-ID ist ein 30-Byte-Cookie; ein JWT kann 500–2000 Byte groß sein.
  • Einfacheres Sicherheitsmodell. Keine Algorithmus-Verwechslungs-Bugs, kein alg: none, keine Verwirrung um die Geheimhaltung des Payloads.
  • Leichter zu skalieren als behauptet. Ein Redis- oder Datenbank-Lookup pro Anfrage fügt höchstens eine Millisekunde hinzu. Das Performance-Argument für JWT verschwindet meist, sobald man es misst.

JWT lohnt seine Komplexität, wenn Sie mehrere Dienste haben, die der Nutzeridentität vertrauen müssen, ohne alle mit einem zentralen Session-Store zu sprechen, oder wenn Sie Tokens an Dritte ausstellen — was genau die Rolle ist, die JWTs in einem OAuth-2.0-/-OpenID-Connect-Ablauf spielen, bei dem der Autorisierungsserver einem Client ein signiertes Access- oder ID-Token übergibt, das der Ressourcenserver offline verifizieren kann. Für eine App, die mit ihrem eigenen Backend spricht, ist das Cookie die langweilige richtige Antwort.

Eine sichere Verifizierungsvorlage

// Node.js, jsonwebtoken-Bibliothek
import jwt from "jsonwebtoken";

function verifyToken(token: string): Payload {
  return jwt.verify(token, publicKey, {
    algorithms: ["RS256"],          // Algorithmus fixieren
    issuer: "https://auth.example.com",   // Issuer fixieren
    audience: "https://api.example.com",  // Audience fixieren
    clockTolerance: 5,              // kleiner Spielraum für Uhren-Drift
  }) as Payload;
}

Fünf Zeilen Optionen, vier davon entschärfen direkt die obigen Angriffe. Die Standardeinstellungen jeder JWT-Bibliothek in 2026 sind sicherer als 2015, doch explizit ist immer noch besser als implizit, wenn der Fehlerfall ein stiller Umgehungsangriff auf die Authentifizierung ist.

Das ehrliche Fazit

Ein JWT zu dekodieren ist trivial — Base64url, splitten, das JSON parsen. Unser In-Browser-Decoder macht das, ohne das Token irgendwohin zu senden, was wichtig ist, weil Tokens oft identifizierende Claims enthalten, die Sie nicht auf eine Drittanbieter-Seite einfügen möchten.

Ein JWT zu verifizieren ist ebenfalls unkompliziert, solange Sie den Algorithmus fixieren, iss, aud und exp validieren und niemals dem Header vertrauen, wie zu verifizieren ist. Wählen Sie HS256, wenn Issuer und Prüfer dieselbe Partei sind; wählen Sie sonst RS256 oder ES256 mit einer geprüften Bibliothek. Und bevor Sie überhaupt zu einem JWT greifen, prüfen Sie, ob ein langweiliges Session-Cookie genügen würde.

Frequently asked questions

Kann ich einem JWT vertrauen, nur weil es sich dekodieren ließ?
Nein – das Dekodieren parst lediglich Header und Payload per Base64url. Jeder kann ein JWT dekodieren; die Sicherheitseigenschaft entsteht ausschließlich durch die Verifizierung der Signatur gegen einen Schlüssel, dem Sie vertrauen. Rufen Sie in produktiven Codepfaden stets verify(token, key) auf, nicht decode(token).
Ist ein JWT verschlüsselt?
Ein Standard-JWT (technisch ein JWS) ist signiert, nicht verschlüsselt – der Payload ist für jeden lesbar, der das Token besitzt. Brauchen Sie Vertraulichkeit, verwenden Sie stattdessen ein JWE (JSON Web Encryption). Legen Sie niemals Passwörter, API-Schlüssel oder andere Geheimnisse in einen JWT-Payload in der Annahme, sie seien verborgen.
Was ist der alg-none-Angriff?
Frühe JWT-Bibliotheken behandelten das alg-Feld im Header als Anweisung – einschließlich des Sonderwerts 'none', der 'keine Signatur' bedeutet. Ein Angreifer konnte den Algorithmus auf none ändern, das Signatursegment weglassen und ein Token präsentieren, das die Bibliothek als gültig akzeptierte. Moderne Bibliotheken lehnen 'none' standardmäßig ab oder verlangen ein explizites Opt-in; wenn Ihr Verifizierungscode verify(token) statt verify(token, expectedAlg, key) sagt, prüfen Sie ihn.
Sollten Access-Tokens kurz- oder langlebig sein?
Kurz – Minuten bis höchstens eine Stunde. JWTs lassen sich ohne Infrastruktur nicht widerrufen (eine explizite Sperrliste hebt die zustandslose Prämisse auf). Eine kurze Access-Token-Lebensdauer in Kombination mit einem langlebigen, serverseitig gespeicherten Refresh-Token ist das Standardmuster: kurze Zeitfenster der Gefährdung bei Token-Leck, einfacher Widerruf durch Ungültigmachen des Refresh-Tokens.
Wann ist JWT tatsächlich die falsche Wahl?
Wenn der Client eine eigene Web-App ist und Sie beide Seiten kontrollieren. Eine reguläre serverseitige Session mit einem HTTP-only-Secure-Cookie ist einfacher, sofort widerrufbar, kleiner bei der Übertragung und immun gegen die meisten JWT-spezifischen Bugs. JWT rechtfertigt seine Komplexität in echten Drittanbieter-Szenarien – föderierte Identität, Microservices, die einem Token vertrauen müssen, ohne pro Anfrage einen zentralen Auth-Dienst aufzurufen – nicht bei 'wir brauchen Login in unserer React-App'.
Was ist der Unterschied zwischen iat und nbf?
iat (issued at) ist der Zeitpunkt der Token-Erstellung – informativ, keine Gültigkeitsgrenze. nbf (not before) ist der früheste Zeitpunkt, ab dem das Token gültig ist – Prüfer müssen das Token vor nbf ablehnen. exp (expiration) ist der späteste Zeitpunkt, zu dem das Token gültig ist. Die meisten Tokens setzen iat und exp; nbf wird vor allem genutzt, wenn Tokens ausgestellt werden, die erst in der Zukunft gültig werden sollen.

Sources & references

Authoritative references cited by this piece. Verified by Buğra Sözeri on the dates shown and re-checked at every deploy.

Related

Published May 31, 2026