JWTs signieren und verifizieren

Ein JWT besteht aus drei base64url-Segmenten — Header, Payload, Signatur — und die ersten beiden sind für jeden lesbar, der das Token besitzt. Ein Token zu dekodieren sagt Ihnen, was es behauptet; nur das dritte Segment sagt Ihnen, ob Sie es glauben sollen. Dieser Artikel handelt von diesem dritten Segment: wie eine Signatur entsteht, wer eine erzeugen kann, wie Verifizierer sie prüfen und an welchem genauen Punkt „verifiziert“ aufhört, „vertrauenswürdig“ zu bedeuten. Sie können jeden Schritt selbst im JWT Signer & Verifier ausführen, der lokal in Ihrem Browser signiert und verifiziert.

Dekodieren ist nicht Verifizieren

Das häufigste Missverständnis über JWTs ist auch das gefährlichste: dass ein Token zu lesen dasselbe sei wie es zu prüfen. Ist es nicht. Header und Payload sind base64url — eine Kodierung, keine Verschlüsselung — also kann jeder, der ein Token abfängt, eines in einem Log findet oder eines aus dem Local Storage eines Browsers kopiert, es in Sekunden dekodieren, ganz ohne Schlüssel. Die Claims darin (für wen es ist, wann es abläuft, welche Rollen es behauptet) sind Behauptungen, und Behauptungen sind billig.

Die Signatur existiert genau deshalb, weil die Claims billig sind. Sie ist eine kryptografische Bindung zwischen Header, Payload und einem Schlüssel, den nur der Aussteller besitzt: Ändern Sie ein Zeichen des Payloads, passt die Signatur nicht mehr. Verifizieren heißt, diese Mathematik noch einmal durchzuspielen — die Signatur über den Header und Payload, die Sie erhalten haben, mit dem Schlüssel des Ausstellers neu zu berechnen und zu vergleichen. Passt sie, ist das Token genau so, wie der Aussteller es signiert hat; passt sie nicht, hat sich etwas geändert, und das Token ist wertlos, egal wie plausibel seine Claims aussehen.

Symmetrisch vs. asymmetrisch: Wer kann ein Token erzeugen

Bei HS256 ist das Geheimnis, das signiert, dasselbe Geheimnis, das verifiziert. Das macht HMAC schnell, einfach und innerhalb eines Dienstes völlig ausreichend — Ihre API signiert ihre eigenen Tokens und prüft sie selbst, und das Geheimnis verlässt nie das Haus. Sobald eine zweite Partei verifizieren muss, bricht das Modell: Ihr das Geheimnis zu geben, gibt ihr die Macht, Tokens zu erzeugen, weil Verifizieren und Signieren dieselbe Operation sind. Ein geleaktes HMAC-Geheimnis ist kein Verifizierungsproblem; es ist ein Fälschungsproblem.

Asymmetrische Algorithmen trennen die beiden Mächte. RS256, ES256 und EdDSA signieren mit einem privaten Schlüssel und verifizieren mit einem öffentlichen Schlüssel, sodass der Aussteller seine öffentlichen Schlüssel veröffentlichen kann — als JWKS — und jeder Verifizierer der Welt Tokens prüfen kann, ohne je die Macht zu besitzen, eines zu fälschen. Diese Trennung macht JWTs erst über Dienste hinweg funktionsfähig: Ihre API vertraut Tokens eines Identity-Providers, weil sie den veröffentlichten Schlüsseln dieses Providers vertraut, nicht weil sie ein Geheimnis mit ihm teilt. Der Preis ist Komplexität — Schlüsselformate, Rotation, kid-Abgleich — und genau das macht der Signer und Verifier auf dieser Seite konkret.

Der Algorithm-Confusion-Angriff

Der klassische JWT-Angriff braucht keine gebrochene Kryptografie — er braucht einen Verifizierer, der dem Token vertraut, seinen eigenen Algorithmus zu nennen. Der Angreifer nimmt ein legitimes RS256-Token, dessen öffentlicher Schlüssel veröffentlicht ist (wie öffentliche Schlüssel es nun einmal sind), und signiert denselben Payload mit HS256 neu, wobei er den öffentlichen Schlüssel selbst als HMAC-Geheimnis verwendet. Ein Verifizierer, der alg aus dem Header liest und seine Verifizierungsroutine danach wählt, wird das gefälschte Token nun mit HMAC „verifizieren“, wobei er den öffentlichen Schlüssel als Geheimnis nutzt — und der öffentliche Schlüssel ist öffentlich.

Die Abwehr ist strukturell, nicht beratend: Der Verifizierer muss die Algorithmen, die er akzeptiert, pinnen, und die Schlüsselfamilie muss zur Algorithmusfamilie passen — ein RSA-Schlüssel kann niemals ein HMAC-Token verifizieren, und ein Geheimnis kann niemals ein RSA-Token verifizieren. Ein Verifizierer, der einen Schlüssel über Familien hinweg umdeutet, ist vom Design her gebrochen, wie korrekt seine Kryptografie auch ist. Darum lehnt der Verifier auf dieser Seite familienfremde Schlüssel mit einer ausdrücklichen Warnung ab, statt die Prüfung zu versuchen: Die Ablehnung ist das Feature.

Wie JWKS und kid-Rotation in der Praxis aussehen

Ein Identity-Provider händigt keinen ewigen Schlüssel aus; er veröffentlicht ein Set — eine JWKS, üblicherweise unter /.well-known/jwks.json — und rotiert Schlüssel, indem er einen neuen hinzufügt und nach einer Übergangsfrist den alten entfernt. Der Header jedes Tokens trägt eine kid, die den Schlüssel nennt, der es signiert hat, sodass ein Verifizierer das Set abruft, den passenden Schlüssel wählt und verifiziert. Tokens, die mit dem alten Schlüssel signiert wurden, funktionieren weiter, bis er entfernt wird — das macht Rotation sicher statt zu einem Stichtag.

Die operativen Details sind der Punkt, an dem es bricht. Verifizierer müssen die JWKS cachen (sie pro Anfrage abzurufen, ist ein selbstverschuldeter Ausfall), den Fall behandeln, dass eine kid unbekannt ist — meist eine Rotation, die der Cache noch nicht gesehen hat, also vor dem Fehlschlagen erneut abrufen — und niemals ein Key-Set von einer nicht vertrauenswürdigen URL akzeptieren: Das Set ist nur so vertrauenswürdig wie der Kanal, über den es kam, weshalb JWKS-Endpunkte hinter HTTPS leben und weshalb der JWKS-Builder hier jede kid als RFC-7638-Thumbprint des Schlüssels selbst ableitet, sodass die ID nicht unabhängig vom Schlüssel gefälscht werden kann.

exp, nbf, iat — und das Uhrenproblem

Eine Signatur beweist, dass ein Token signiert wurde; sie sagt nichts darüber, ob es noch eingelöst werden sollte. Das ist die Aufgabe der Zeit-Claims: exp (wann es ungültig wird), nbf (wann es gültig wird), iat (wann es ausgestellt wurde). Ein Verifizierer, der die Signatur prüft und die Uhr überspringt, akzeptiert ein Token für immer — einschließlich eines, das vor einem Jahr gestohlen wurde. Kurze Lebensdauern sind die praktische Antwort: Access-Tokens, die in Minuten gemessen werden, Refresh-Tokens, die das langlebige Vertrauen tragen, und Widerruf, der dadurch geschieht, dass man einfach nicht neu ausstellt.

Uhren gehen auseinander, also erlauben Verifizierer ein wenig Spielraum — typischerweise ein oder zwei Minuten Toleranz bei exp und nbf — aber Toleranz ist ein Zugeständnis an Drift, keine Lizenz, das Ablaufdatum zu ignorieren. Ein Gerät, dessen Uhr Minuten danebenliegt, erzeugt Tokens, die sein eigener Server ablehnt, und ein Token mit einem iat in der Zukunft ist ein Fälschungssignal, kein Rundungsfehler. Dieselbe Uhrendisziplin gilt für die Werkzeuge: Der Verifier prüft das Ablaufdatum gegen die Uhr, die Sie sehen können, und sagt offen, wenn ein Token abgelaufen ist, statt nur „ungültig“.

Was „verifiziert“ beweist — und was nicht

Ein verifiziertes Token beweist drei Dinge und nichts weiter: Header und Payload sind genau so, wie sie signiert wurden, der Signierer besaß den Schlüssel, und das Token liegt innerhalb seines Gültigkeitsfensters. Es beweist nicht, dass der Aussteller der ist, für den Sie ihn halten — das hängt davon ab, woher der Schlüssel stammt, und ein Schlüssel, der von einer vom Angreifer kontrollierten URL abgerufen wurde, verifiziert die Tokens eines Angreifers perfekt. Es beweist nicht, dass die Claims wahr sind — eine Signatur authentifiziert die Aussage, nicht die Fakten dahinter. Und es beweist nicht, dass das Token nicht gestohlen wurde: Ein gültiges Token, das von irgendjemandem vorgelegt wird, wird akzeptiert, weshalb Tokens kurzlebig und eng gefasst sind.

Die Vertrauensentscheidung liegt also außerhalb des Tokens. Ihre API vertraut einem Token, weil sie den Schlüssel von der Well-known-URL ihres Identity-Providers über HTTPS abgerufen hat, weil das iss zu dem Provider passt, den sie konfiguriert hat, und weil das aud zu dem Dienst passt, der sie ist. Verifizierung ist die letzte Prüfung, nicht die erste: Schlüsselherkunft, Aussteller, Audience, dann Signatur, dann Zeit. Ein Tool, das „GÜLTIG“ meldet, ohne diesen Kontext, sagt die Wahrheit über die Mathematik und nichts über das Vertrauen — weshalb der Verifier dieser Seite das auf der Seite sagt, direkt neben dem Urteil.

Häufig gestellte Fragen

Kann ich ein Token ohne den Schlüssel verifizieren?

Nein — die Verifizierung braucht den öffentlichen Schlüssel des Ausstellers (oder das gemeinsame Geheimnis, bei HMAC). Dekodieren braucht keinen Schlüssel, und genau das ist die Verwechslung, die es zu vermeiden gilt: Ein dekodiertes Token ist lesbar, nicht verifiziert. Wenn der Aussteller eine JWKS veröffentlicht, rufen Sie sie von der Well-known-URL ab und gleichen Sie die kid des Tokens ab; der Verifier auf dieser Seite macht das für Sie.

Warum schlägt mein RS256-Token mit meinem Geheimnis fehl?

Weil ein Geheimnis und ein RSA-Schlüssel verschiedene Familien sind, und ein solider Verifizierer sich weigert, sie zu mischen. RS256-Tokens werden mit dem öffentlichen RSA-Schlüssel des Ausstellers verifiziert — PEM, JWK oder JWKS — niemals mit einem Textgeheimnis. Ein Geheimnis zu nutzen, wäre der Algorithm-Confusion-Angriff: Der öffentliche Schlüssel ist öffentlich, also könnte jeder Tokens fälschen, wenn die HMAC-Verifizierung ihn als Geheimnis akzeptierte.

Wie lange sollte ein JWT leben?

Minuten, nicht Tage, bei Access-Tokens: 5–15 Minuten sind der übliche Bereich, wobei ein Refresh-Token die längere Sitzung trägt. Eine kurze Lebensdauer begrenzt den Schaden eines gestohlenen Tokens auf das Fenster, bevor es abläuft, und macht Widerruf zu einer Frage des Nicht-Neuausstellens statt des Pflegens einer Sperrliste. Tokens, die wochenlang leben, sind Session-Cookies mit schlechterem Werkzeug.

Quellen