Technologie

Was ein JWT ist, was darin steckt und warum Dekodieren nicht Validieren ist

Aufbau eines JSON Web Tokens (Header, Payload und Signatur), welche Claims es trägt, wie man es im Browser liest, häufige Sicherheitsfehler und wo man es speichert.

Ein JWT (JSON Web Token, RFC 7519) ist eine Zeichenkette aus drei durch Punkte getrennten Teilen, header.payload.signatur, die Aussagen (Claims) über einen Nutzer oder eine Sitzung so transportiert, dass der Empfänger prüfen kann, dass niemand sie verändert hat. Die ersten beiden Teile sind Base64URL-kodiertes JSON: Jeder kann sie lesen. Der dritte ist eine Signatur, die nur erzeugen kann, wer den Schlüssel besitzt. Deshalb ist ein JWT zu dekodieren nicht dasselbe wie es zu validieren: Den Inhalt zu lesen ist trivial; ihm zu vertrauen verlangt, Signatur, Ablauf und Aussteller zu prüfen.

Dieser Artikel geht den Aufbau eines Tokens durch, die üblichen Claims, wie man es inspiziert, die klassischen Sicherheitsfehler und wo man es in einer Webanwendung speichert.

Aufbau eines Tokens

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFpbXJhbiIsImlhdCI6MTc5MTI4MDAwMCwiZXhwIjoxNzkxMjgzNjAwfQ.<signatur>
  1. Header ({"alg":"HS256","typ":"JWT"}): Signaturalgorithmus und Token-Typ.
  2. Payload ({"sub":"1234","name":"Aimran","iat":1791280000,"exp":1791283600}): die Claims.
  3. Signatur: das Ergebnis der Signierung von base64url(header) + "." + base64url(payload) mit dem Algorithmus aus dem Header. Bei HS256 ist das ein HMAC-SHA256 mit einem gemeinsamen geheimen Schlüssel; bei RS256 oder ES256 eine asymmetrische Signatur mit privatem Schlüssel, die mit dem öffentlichen Schlüssel geprüft wird.

Die Kodierung ist Base64URL (eine Base64-Variante mit - und _ statt + und /, ohne =-Füllzeichen), die wir in Was ist Base64 behandeln. Das ist keine Verschlüsselung: Der Payload lässt sich mit einer Codezeile lesen.

Die üblichen Claims

Claim Name Zweck
iss Issuer Wer das Token ausgestellt hat (dein Authentifizierungsserver).
sub Subject Auf wen es sich bezieht (die Nutzer-ID).
aud Audience Für wen es bestimmt ist (die API, die es akzeptieren soll).
exp Expiration Unix-Zeitpunkt, ab dem es nicht mehr gültig ist.
nbf Not before Unix-Zeitpunkt, vor dem es noch nicht gültig ist.
iat Issued at Wann es ausgestellt wurde.
jti JWT ID Eindeutige Kennung, nützlich zum Widerrufen.

Laut RFC sind alle optional, aber exp sollte immer gesetzt sein: Ein Token ohne Ablauf gilt für immer, wenn es abhandenkommt. Eigene Claims (role, plan) sind erlaubt, mit einer Regel: nie sensible Daten, denn der Payload ist für jeden öffentlich, der das Token hat.

Ein JWT inspizieren

Um zu sehen, was ein Token enthält, genügt es, die ersten beiden Teile zu dekodieren. Der JWT-Decoder von AIMRAN Tools zeigt Header und Payload, übersetzt iat, exp und nbf in lesbare Daten und meldet, ob das Token abgelaufen oder noch nicht gültig ist, alles im Browser, ohne das Token an einen Server zu senden. Fügst du auch das Secret ein, prüft er die HMAC-Signatur. In Node.js ohne Bibliotheken:

const [header, payload] = token.split('.');
const decode = (part) => JSON.parse(Buffer.from(part, 'base64url').toString('utf8'));
console.log(decode(header), decode(payload));

Dieser Code liest, er validiert nicht. Zum Validieren nutze eine etablierte Bibliothek (jose oder jsonwebtoken in Node; PyJWT in Python), die die Signatur mit dem von dir erwarteten Algorithmus, den Ablauf und den Aussteller prüft.

Sicherheitsfehler, die immer wiederkehren

RFC 8725, die offiziellen Best Practices, deckt die meisten ab:

  1. Dem Payload vertrauen, ohne die Signatur zu prüfen. Jeder kann ein Token mit "role":"admin" fälschen. Prüft deine API die Signatur nicht, hat sie gerade Rechte verschenkt.
  2. Den Algorithmus akzeptieren, den der Header nennt. Der klassische alg: none-Angriff schickt ein unsigniertes Token und hofft, dass der Server es akzeptiert. Die Bibliothek darf nur die von dir konfigurierten Algorithmen prüfen.
  3. HS256/RS256-Verwechslung. Nutzt der Server RS256 (öffentlicher Schlüssel) und akzeptiert auch HS256, kann ein Angreifer mit dem öffentlichen Schlüssel signieren, als wäre er das HMAC-Secret. Gleiche Lösung: geschlossene Algorithmusliste.
  4. Schwache HMAC-Secrets. Ein kurzes Secret lässt sich offline per Brute Force knacken, weil der Angreifer das Token besitzt. Nutze zufällige Secrets mit mindestens 256 Bit.
  5. Ewige Tokens. Kein exp oder Laufzeiten von Monaten. Üblich: Access-Tokens für Minuten plus ein Refresh-Token zur Erneuerung.
  6. Sensible Daten im Payload. E-Mails, Ausweisnummern, Adressen: Der Payload ist nicht verschlüsselt. Brauchst du Vertraulichkeit, gibt es JWE (verschlüsselte Tokens), was aber selten nötig ist.

Wo das Token im Browser liegen sollte

Zwei Optionen mit unterschiedlichen Kompromissen:

  • Cookie HttpOnly; Secure; SameSite: JavaScript kann es nicht lesen, ein XSS stiehlt es also nicht; der Browser sendet es automatisch; gegen CSRF braucht es Schutz (SameSite=Lax oder Strict deckt die meisten Fälle ab). Das ist die übliche Empfehlung für klassische Webanwendungen.
  • localStorage oder Arbeitsspeicher: Jedes Skript der Seite kann es lesen, ein XSS kompromittiert es also vollständig. Sinnvoll nur für Clients ohne Cookies (native Apps) oder wenn die API auf einer anderen Domain liegt und das Token nur im Speicher lebt, solange der Tab offen ist.

In beiden Fällen senkt eine strikte Content-Security-Policy das XSS-Risiko, das die eigentliche Bedrohung hinter dieser Debatte ist.

JWT gegenüber klassischen Sitzungen

Ein JWT erlaubt der API, die Identität ohne Datenbankabfrage bei jeder Anfrage zu prüfen: Alle Informationen reisen im Token mit. Dieser Vorteil hat einen Preis: Ein Token lässt sich nicht widerrufen, bevor es abläuft, es sei denn, du führst eine Sperrliste (womit die Datenbankabfrage zurückkehrt). Für eine Anwendung mit einem einzigen Server ist eine klassische Sitzung (opake Kennung im Cookie, Daten auf dem Server) einfacher und leichter zu invalidieren. JWTs glänzen, wenn mehrere Dienste dieselbe Identität akzeptieren müssen, ohne einen Sitzungsspeicher zu teilen.

Fazit

Ein JWT besteht aus drei Base64URL-Blöcken: Header, Payload und Signatur. Die ersten beiden kann jeder lesen; nur die Signatur, geprüft mit dem richtigen Algorithmus und dem richtigen Schlüssel, erlaubt es, ihnen zu vertrauen. Setze immer exp, begrenze die akzeptierten Algorithmen, halte sensible Daten aus dem Payload und speichere es, wenn möglich, in einem HttpOnly-Cookie. Und wenn du wissen willst, was ein Token enthält, dekodiere es ohne Scheu: Das ist tatsächlich umsonst.

Quellen und Referenzen

  1. RFC 7519: JSON Web Token (JWT) rfc-editor.org
  2. RFC 7515: JSON Web Signature (JWS) rfc-editor.org
  3. RFC 8725: JSON Web Token Best Current Practices rfc-editor.org
  4. OWASP Cheat Sheet: JSON Web Token for Java cheatsheetseries.owasp.org
  5. MDN: HTTP-Cookies verwenden developer.mozilla.org

Passende Tools

Herramientas gratuitas de AIMRAN Tools que funcionan en tu navegador, sin registro.

Ver todas las herramientas
  • JWT-Decoder

    Liest Header, Payload und Ablauf eines Tokens und prüft HMAC-Signaturen mit deinem Secret, direkt im Browser.

Artikel in Technologie