Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Identity & Access Management Glossar

JSON Web Token (JWT)

JSON Web Tokens sind kompakte, signierte Token zum sicheren Übertragen von Benutzeridentität und Claims zwischen Systemen. JWTs werden als Basis für moderne API-Authentifizierung und Single Sign-On genutzt - sind aber bei falscher Implementierung eine häufige Sicherheitslücke.

JSON Web Tokens (JWT) sind ein offener Standard (RFC 7519) für den sicheren Austausch von Informationen als JSON-Objekte. Sie werden von OAuth 2.0 und OpenID Connect als ID Tokens und Access Tokens genutzt - und sind gleichzeitig eine der häufigsten Quellen von Auth-Bugs in modernen Webanwendungen.

JWT Struktur - Header.Payload.Signature

Ein JWT besteht aus drei Base64URL-kodierten Teilen, die durch Punkte getrennt sind. Das folgende Beispiel zeigt einen dekodiertes JWT:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTQ4MDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Der Header (nach Base64URL-Decode) enthält den verwendeten Algorithmus:

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

Der Payload (nach Base64URL-Decode) enthält die Claims:

{
  "sub": "user_123",
  "iss": "https://auth.firma.de",
  "aud": "api.firma.de",
  "iat": 1711393600,
  "exp": 1711480000,
  "role": "admin",
  "email": "max@firma.de"
}

Die Signature wird berechnet als:

RSASHA256(base64url(header) + "." + base64url(payload), privateKey)

Wichtig: Der Payload ist NUR Base64URL-kodiert, nicht verschlüsselt. Jeder kann den Payload dekodieren und lesen. Die Signatur verhindert Manipulation, aber nicht das Lesen. Keine sensitiven Daten wie Passwörter oder Sozialversicherungsnummern in den Payload schreiben.

JWT in der Praxis: Ausgabe und Validierung

Login-Flow mit JWT

Der Ablauf beginnt damit, dass der Client Credentials an den Auth-Server sendet. Der Server prüft die Credentials, erstellt ein signiertes JWT und antwortet mit Access Token, Refresh Token und der Gültigkeitsdauer (expires_in: 3600).

API-Request mit JWT

Der Client sendet das Token im Authorization: Bearer-Header. Der API-Server validiert dabei vier Punkte: Signatur prüfen, exp > jetzt, iss == bekannt und aud == diese API.

Die Validierung in Node.js sieht so aus:

import { verify } from 'jsonwebtoken';

function validateJWT(token: string): JWTPayload {
  try {
    const payload = verify(token, publicKey, {
      algorithms: ['RS256'],         // Nur RS256 oder ES256!
      issuer: 'https://auth.firma.de',
      audience: 'api.firma.de',
    });
    return payload as JWTPayload;
  } catch (err) {
    throw new UnauthorizedError('Invalid token');
  }
}

JWT Algorithmen - Kritischer Vergleich

Symmetrische Algorithmen (HMAC)

AlgorithmusEigenschaftenProblem
HS256Selber Schlüssel für Signatur und ValidierungAuth-Server und API müssen Schlüssel teilen; jeder API-Server der validiert, könnte auch JWTs ausstellen
HS512Stärker als HS256Gleiches Grundproblem

HMAC-Algorithmen sind nur sicher bei Single-Service-Setups, wo ausschließlich eine Komponente validiert.

Asymmetrische Algorithmen (empfohlen)

AlgorithmusEigenschaftenEmpfehlung
RS256 (RSA + SHA256)Private Key nur Auth-Server, Public Key alle API-Server via JWKSStandard für OAuth 2.0 / OIDC
ES256 (ECDSA + SHA256)Elliptische Kurven, kleinere Schlüssel, gleiche Sicherheit wie RS256, schneller als RSAEmpfohlen für neue Systeme

GEFÄHRLICH - Niemals verwenden

alg: "none" (unsigned JWT): Ein JWT ohne Signatur. Einige Bibliotheken akzeptieren das - dies ist die größte historische JWT-Schwachstelle. Angreifer ändern den Header auf "alg":"none" und manipulieren den Payload beliebig.

Algorithm Confusion (alg: "HS256" wenn RS256 erwartet wird): Angreifer signiert mit dem Public Key als HS256-Secret. Wenn der Server den alg-Header ungeprüft liest, wird der Token als gültig akzeptiert. Fix: Algorithmus immer im Code fixieren, nie aus dem Header lesen.

JWT Sicherheitslücken

1. Algorithm Confusion (alg-Swap)

Der Angreifer ändert den Header von {"alg": "RS256"} auf {"alg": "HS256"} und signiert mit dem öffentlich verfügbaren RS256 Public Key als HMAC-Secret. Der Server liest den alg-Header und prüft mit dem Public Key als HS256-Secret - der Token gilt als valid, und der Angreifer kann beliebige Claims setzen.

Fix: Algorithmus hardcoden:

verify(token, publicKey, { algorithms: ['RS256'] })  // NIEMALS ['RS256', 'HS256']!

2. “none” Algorithm

Token mit alg: "none" benötigen keine Signatur, der Payload kann beliebig manipuliert werden (z.B. role: "admin").

Fix: Bibliothek prüfen - verify() muss eine Algorithmus-Liste erzwingen.

3. Fehlende Expiration (exp)

Ein JWT ohne exp-Claim oder mit sehr langer Gültigkeit bleibt nach einem Diebstahl dauerhaft gültig.

Fix: exp immer setzen, maximal 1 Stunde für Access Tokens, Refresh Tokens maximal 7-30 Tage.

4. Signatur nicht validiert

Manche APIs prüfen nur ob ein Token vorhanden ist, nicht ob die Signatur stimmt - decode ist nicht dasselbe wie verify.

  • Falsch: jwt.decode(token) - nur dekodieren, keine Signaturprüfung
  • Richtig: jwt.verify(token, publicKey)

5. Sensitive Daten im Payload

Da der JWT-Payload nur Base64URL-kodiert ist, sind Passwort-Hashes, Sozialversicherungsnummern oder Kontonummern für jeden sichtbar. Im Payload dürfen nur nicht-sensitive Claims stehen, die für Auth-Entscheidungen nötig sind.

6. Token in localStorage (Browser)

Daten in localStorage sind per JavaScript lesbar - ein XSS-Angriff stiehlt den Token.

Besser: httpOnly Secure Cookie (unzugänglich für JavaScript). Für SPAs: In-Memory-Speicher plus Refresh Token in httpOnly Cookie.

7. Fehlende Audience (aud) Prüfung

Ein Token für Service A (aud: "service-a") wird an Service B gesendet, der ihn akzeptiert - dies nennt sich Token Substitution.

Fix: aud-Claim immer explizit prüfen, bei verify() angeben.

JWKS - Public Keys dynamisch laden

JWKS (JSON Web Key Set) ist der Standard für Public Key Distribution. Der Auth-Server publiziert seinen öffentlichen Schlüssel unter GET /.well-known/jwks.json:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "key-2024-01",
      "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2...",
      "e": "AQAB"
    }
  ]
}

Der API-Server lädt die Keys beim Start und cached sie:

import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://auth.firma.de/.well-known/jwks.json')
);

const { payload } = await jwtVerify(token, JWKS, {
  issuer: 'https://auth.firma.de',
  audience: 'api.firma.de',
});

Key Rotation: Der Auth-Server kann Keys rotieren - der kid-Claim im JWT-Header identifiziert welchen Key zu verwenden ist. Alte Tokens bleiben gültig solange exp nicht abgelaufen ist. Der JWKS-Endpoint stellt neue Keys bereit, API-Server cachen Keys mit z.B. 10 Minuten TTL.

JWT Lifetime und Refresh Token Strategie

Empfohlene Lebensdauern

TokenDauerBegründung
Access Token15-60 MinutenKurz - falls gestohlen, schnell wertlos; kein Revocation-Problem
Refresh Token7-30 TageZum Erneuern von Access Tokens; in sicherer Storage

Refresh Token Rotation

Bei jedem Access-Token-Refresh wird ein neuer Refresh Token ausgegeben und der alte invalidiert. Dies ermöglicht die Erkennung von Token-Diebstahl: Wenn ein gestohlener Refresh Token benutzt wird, während der legitime Nutzer den alten Token noch als aktiv kennt, erkennt das System zwei aktive Refresh Tokens und revoziert beide.

POST /auth/refresh
Cookie: refresh_token=eyJ...

Response:
{ "access_token": "eyJ...", "expires_in": 900 }
Set-Cookie: refresh_token=eyJ...; HttpOnly; Secure; SameSite=Strict

JWT vs. Session Tokens - Wann was?

EigenschaftJWTSession Token
ZustandStateless (API validiert)Stateful (DB-Lookup)
RevocationSchwierig (bis exp!)Sofort (Token aus DB löschen)
SkalierungEinfach (kein DB-Hit)DB-Zugriff bei jedem Request
Größe~400-1000 Byte32 Byte (nur ID)
PayloadClaims direkt im TokenClaims in der Session-Tabelle

Wann JWT verwenden

  • Microservices (Service B validiert ohne Service A zu fragen)
  • API-Ökosystem mit mehreren Audiences
  • OAuth 2.0 / OIDC (Standard definiert JWT)

Wann Session Tokens verwenden

  • Traditionelle Web-Apps mit serverseitigem Rendering
  • Sofortige Revocation nötig (z.B. “Account gesperrt”)
  • Kleinere Datenmenge pro Request gewünscht
  • Einfachere Implementierung bevorzugt

Hybrid-Ansatz (empfohlen für Web-Apps)

  • Access Token: kurzes JWT (15 Min)
  • Session: httpOnly Cookie mit Refresh Token
  • Revocation via Refresh Token Datenbank

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung