In einem Satz
Ein JSON Web Token (JWT) ist eine kompakte, URL-sichere Methode, um Claims (Ansprüche) zwischen zwei Parteien zu übertragen. Es wird typischerweise für Authentifizierung und Autorisierung auf eine Weise verwendet, die verifizierbar und vertrauenswürdig ist.
Welches Problem es löst
In den alten Zeiten – sagen wir, den frühen 2000ern – erstellte ein Server eine „Session“ für dich, wenn du dich auf einer Website eingeloggt hast. Das war wie eine kleine Datei auf dem Server, die besagte: „User 123 ist eingeloggt und hat ein Gummihuhn in seinen Warenkorb gelegt.“ Der Server gab deinem Browser einen winzigen Cookie mit einer Session-ID, quasi wie eine Garderobenmarke. Bei jeder folgenden Anfrage zeigte dein Browser die Marke vor, der Server schaute sie nach, fand deine Datei und wusste wieder, wer du warst.
Das funktionierte für einen einzelnen, monolithischen Server ganz gut. Aber dann explodierte das Web. Wir bekamen Microservices, Single-Page Applications (SPAs) und mobile Apps, die alle mit demselben Backend sprachen. Jetzt geht deine Login-Anfrage vielleicht an Server A, aber deine nächste Anfrage, um dein Profil abzurufen, geht vielleicht an Server B. Wie weiß Server B von der Session-Datei auf Server A?
Man könnte einen User zwingen, immer mit demselben Server zu sprechen („Sticky Sessions“), aber das ist ein Flaschenhals. Man könnte eine zentrale Session-Datenbank (wie Redis) einrichten, die alle Server gemeinsam nutzen, aber das ist ein weiteres Infrastruktur-Teil, das verwaltet werden muss und eine weitere Fehlerquelle darstellt.
Das Kernproblem ist die Zustandsbehaftung (Statefulness). Der Server muss sich an dich erinnern.
JWTs (ausgesprochen „Jots“) stellten diese Idee auf den Kopf. Was wäre, wenn der User seinen eigenen Identitätsnachweis mit sich führen könnte, wie einen Reisepass? Das Token selbst würde alle Informationen enthalten, die der Server benötigt: wer der User ist, was er tun darf und wann sein Zugriff abläuft. Der Server muss sich zwischen den Anfragen an nichts erinnern. Das ist zustandslose (stateless) Authentifizierung und der Schlüssel zum Aufbau skalierbarer, verteilter Systeme. Der Server muss nur prüfen, ob der Reisepass (das JWT) gültig und nicht gefälscht ist.
Wie es unter der Haube funktioniert
Ein JWT ist kein undurchschaubarer Kauderwelsch. Es ist ein sehr spezifischer, strukturierter String, der aus drei Teilen besteht, die durch Punkte (.) getrennt sind.
xxxxx.yyyyy.zzzzz
Schauen wir uns die einzelnen Teile genauer an.
Der Header (Das „Typ“-Etikett)
Der erste Teil ist der Header. Es ist ein einfaches JSON-Objekt, das Metadaten über das Token selbst enthält, hauptsächlich den verwendeten Signaturalgorithmus und den Typ des Tokens.
{
"alg": "HS256",
"typ": "JWT"
}
alg: Der Signaturalgorithmus.HS256bedeutet, dass dieses Token mit HMAC-SHA256 signiert ist, einem symmetrischen Algorithmus (mehr dazu gleich). Andere gängige Optionen sindRS256(unter Verwendung eines RSA-Schlüsselpaars aus öffentlichem/privatem Schlüssel).typ: Der Token-Typ. Bei JWTs ist dies einfach „JWT“.
Dieses JSON wird dann Base64Url-kodiert, um den ersten Teil des Tokens zu erzeugen. Base64 ist ein Kodierungsschema, keine Verschlüsselung. Es wandelt Binärdaten einfach in einen Textstring um, der sicher über das Web übertragen werden kann. Stell es dir so vor, als würdest du „DIES IST EINE POSTKARTE“ auf die Rückseite einer Postkarte schreiben – jeder, der sie abfängt, kann sie lesen.
Der Payload (Die „Claims“-Abteilung)
Der zweite Teil ist der Payload. Das ist das eigentliche Goldstück. Es ist ein weiteres JSON-Objekt, das die „Claims“ enthält, also Aussagen über den Benutzer (das „Subjekt“) und andere nützliche Daten.
{
"sub": "10987-23456-98765",
"name": "Grace Hopper",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}
Claims gibt es in drei Geschmacksrichtungen:
- Registered Claims (Registrierte Claims): Dies ist ein Satz vordefinierter, empfohlener Claims, um Interoperabilität zu gewährleisten. Sie sind nicht zwingend erforderlich, aber super nützlich.
| Claim | Name | Beschreibung |
|---|---|---|
iss |
Issuer (Aussteller) | Wer das Token ausgestellt hat (z. B. https://api.mycoolsite.com). |
sub |
Subject (Subjekt) | Der User oder die Entität, um die es im Token geht (z. B. eine User-ID). |
aud |
Audience (Zielgruppe) | Für wen das Token bestimmt ist (z. B. https://api.mycoolsite.com). |
exp |
Expiration Time (Ablaufzeit) | Wann das Token abläuft. Ein numerischer Unix-Zeitstempel (Sekunden seit der Epoche). |
iat |
Issued At (Ausstellungszeit) | Wann das Token ausgestellt wurde. Ebenfalls ein Unix-Zeitstempel. |
- Public Claims (Öffentliche Claims): Das sind benutzerdefinierte Claims, die du erstellst, aber um Namenskollisionen zu vermeiden, sollten sie im IANA JSON Web Token Claims Registry definiert sein oder eine URI sein, die einen kollisionssicheren Namespace enthält.
- Private Claims (Private Claims): Dies sind die häufigsten benutzerdefinierten Claims, die erstellt werden, um Informationen zwischen Parteien auszutauschen, die sich auf ihre Verwendung einigen (wie
admin: truein unserem Beispiel). Hier packst du deine anwendungsspezifischen Daten rein.
Genau wie der Header wird auch der gesamte Payload-JSON Base64Url-kodiert, um den zweiten Teil des JWT zu bilden. Nochmals, dies ist nicht verschlüsselt. Gib niemals sensible Informationen wie Passwörter in den Payload.
Die Signatur (Das manipulationssichere Siegel)
Dies ist der Teil, der für die Sicherheit sorgt. Die Signatur wird verwendet, um zu überprüfen, dass der Absender des JWT der ist, für den er sich ausgibt, und um sicherzustellen, dass die Nachricht unterwegs nicht verändert wurde.
Sie wird erstellt, indem man den kodierten Header, den kodierten Payload, einen geheimen Schlüssel (Secret) nimmt und sie durch den im Header angegebenen Algorithmus jagt. Für unser HS256-Beispiel sieht der Prozess so aus:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
dein-256-bit-geheimnis
)
Die Pointe: Es wird ein Secret verwendet, das nur der Server kennt. Wenn der Server ein JWT empfängt, führt er genau dieselbe Berechnung mit dem empfangenen Header und Payload erneut durch. Wenn die von ihm erzeugte Signatur mit der Signatur auf dem Token übereinstimmt, weiß der Server zwei Dinge:
- Authentizität: Das Token wurde von jemandem erstellt, der den geheimen Schlüssel kennt (d. h. der Server selbst).
- Integrität: Der Header und der Payload wurden nicht manipuliert. Hätte ein Angreifer im Payload
"admin": falsezu"admin": true"geändert, würde die Signatur nicht mehr übereinstimmen.
Diese Signatur ist das manipulationssichere holografische Siegel auf unserem Reisepass.
Geschichten aus der Praxis
Das Labyrinth der Microservices
Ein schnell wachsendes E-Commerce-Unternehmen, „ScaleFast“, beschloss, sein riesiges, monolithisches Backend in eine Flotte von Microservices aufzuteilen: einen für Benutzer, einen für Bestellungen, einen für das Inventar usw. Das alte System verwendete eine serverseitige Session. Aber wie weiß in der neuen Welt der OrderService, dass eine Anfrage wirklich von einem eingeloggten Benutzer kam, ohne bei jeder einzelnen Anfrage den UserService anrufen zu müssen? Das wäre langsam und würde den Zweck der Entkopplung zunichtemachen.
Die Lösung war JWT. Wenn sich ein Benutzer anmeldet, stellt der neue AuthService ein JWT aus, das die userId und seine roles enthält. Der Browser des Benutzers fügt dieses JWT dann in den Authorization-Header jeder Anfrage an andere Microservices ein. Der OrderService und der InventoryService müssen nicht mit dem AuthService sprechen; sie müssen nur den gemeinsamen geheimen Schlüssel kennen. Sie können die Signatur des JWTs unabhängig überprüfen, der userId darin vertrauen und die Anfrage bearbeiten.
Lektion: JWTs sind die Lingua Franca der Microservice-Authentifizierung und ermöglichen es Diensten, zustandslos und unabhängig verifizierbar zu sein.
Die Single-Page-App-Saga
Eine Entwicklerin namens Alex baute ein schickes React-Dashboard. Das Frontend war eine Single-Page Application (SPA), die von einem statischen Host bereitgestellt wurde, und es sprach mit einer separaten Backend-API. Alex kämpfte mit der altmodischen Cookie-basierten Authentifizierung und stieß auf einen Albtraum von Cross-Origin Resource Sharing (CORS)-Problemen, da Frontend und Backend auf unterschiedlichen Domains lagen.
Das Team wechselte zu JWT. Wenn sich ein Benutzer jetzt mit seinem Benutzernamen und Passwort anmeldet, sendet die API ein JWT zurück. Alex' React-App speichert dieses Token im Speicher und hängt es an jeden API-Aufruf an: Authorization: Bearer <the-jwt>. Das API-Backend ist zustandslos; es überprüft bei jeder eingehenden Anfrage nur das Bearer-Token. Keine CORS-Cookie-Kopfschmerzen mehr.
Lektion: JWTs bieten einen sauberen, portablen Berechtigungsnachweis, der wunderbar zur Entkopplung moderner Frontend-Anwendungen von Backend-APIs funktioniert.
Häufige Fehler und Fallstricke
- Sensible Daten in den Payload packen. Stopp! Der Payload ist Base64Url-kodiert, was trivial umkehrbar ist. Er ist nicht verschlüsselt. Jeder, der das Token in die Hände bekommt, kann den Payload lesen. Behandle ihn wie eine Postkarte, nicht wie einen versiegelten Brief.
- Vergessen, die Signatur zu überprüfen. Was nützen die Sicherheitsmerkmale eines Reisepasses, wenn der Grenzbeamte sie nicht überprüft? Nur den Payload zu dekodieren und seinem Inhalt zu vertrauen, ohne die Signatur zu verifizieren, ist eine katastrophale Sicherheitslücke. Ein Angreifer könnte jeden beliebigen Payload fälschen.
- Dem
alg-Header blind vertrauen. Eine bekannte frühere Sicherheitslücke bestand darin, dass Angreifer ein Token erstellten und den Header auf{"alg": "none"}änderten. Einige falsch konfigurierte Bibliotheken sahen „none“ und „verifizierten“ die Signatur, indem sie, nun ja, nichts taten und das gefälschte Token als gültig akzeptierten. Lass deinen Server immer einen spezifischen, erwarteten Algorithmus erzwingen (z. B.HS256). - Deinen symmetrischen geheimen Schlüssel leaken. Bei HMAC-Algorithmen wie
HS256ist der geheime Schlüssel der Schlüssel zum Königreich. Wenn er leakt, kann jeder Tokens für jeden Benutzer mit beliebigen Berechtigungen fälschen. Hüte ihn wie ein Passwort. - Keinen Ablauf-Claim (
exp) setzen. Ein Token, das ewig lebt, ist ein riesiges Sicherheitsrisiko. Wenn es jemals kompromittiert wird, kann ein Angreifer es auf unbestimmte Zeit verwenden. Setze immer eine angemessen kurze Ablaufzeit und verwende einen Refresh-Token-Mechanismus für länger andauernde Sessions.
Warum du es auf dem Schirm haben solltest
Du solltest an „JWT“ denken, wann immer du mit Authentifizierung oder Autorisierung in einer verteilten Umgebung zu tun hast.
- Du baust eine API für eine Single-Page App (SPA) oder einen mobilen Client.
- Du entwirfst eine Microservice-Architektur, in der Dienste den Anfragen der anderen vertrauen müssen.
- Du benötigst eine zustandslose Authentifizierung, die horizontal skaliert werden kann, ohne einen gemeinsamen Session-Speicher.
- Du implementierst einmalige Autorisierungsflüsse wie Links zum Zurücksetzen von Passwörtern oder E-Mail-Verifizierungen, bei denen ein in sich geschlossenes, verfallbares Token perfekt passt.
Es ist der moderne Standard zur sicheren Darstellung von Claims, und das Verständnis seiner Stärken – und Schwächen – ist für den heutigen Entwickler nicht verhandelbar.
Tauche tiefer ein
- RFC 7519: Die offizielle Spezifikation für JSON Web Token (JWT). Die Quelle der Wahrheit.
- jwt.io: Eine fantastische Ressource mit einem Live-Debugger und einer Liste von Bibliotheken für fast jede Sprache.
- OWASP JWT Cheat Sheet: Ein unverzichtbarer Leitfaden zu den Best Practices und Fallstricken bei der Verwendung von JWTs (die Ratschläge sind sprachunabhängig).
- MDN Web Docs: Authorization header: Erfahre mehr über das
Bearer-Authentifizierungsschema, das häufig zur Übertragung von JWTs verwendet wird. - Wikipedia: JSON Web Token: Ein guter, allgemeiner Überblick über das Konzept und seine Geschichte.