Melden Sie einen Nutzer in einer session-basierten App ab, vergisst der Server ihn bei der nächsten Anfrage. Melden Sie ihn in einer JWT-basierten App ab, bleibt das Token, das er besitzt, gültig, bis es abläuft – es sei denn, Sie haben einen zusätzlichen Mechanismus dafür gebaut. Dieser Unterschied ist die eigentliche Entscheidung hinter „JWT vs. Session". Der Rest folgt daraus.
Was eine Session tatsächlich speichert
Eine Session hält den Zustand des Nutzers auf dem Server und gibt dem Browser nur einen Schlüssel zum Nachschlagen.
- Der Nutzer meldet sich mit einem Passwort an.
- Der Server legt einen Session-Datensatz an (Nutzer-ID, Rollen, was auch immer er braucht) und speichert ihn im Speicher, in einer Datenbank oder in Redis, indiziert mit einer zufälligen Session-ID.
- Der Server sendet diese ID als Cookie an den Browser, markiert mit
HttpOnly, damit client-seitiges JavaScript es nicht lesen kann. - Bei jeder Anfrage sendet der Browser das Cookie automatisch zurück. Der Server sucht die ID im Store und holt die Session-Daten.
Das Cookie trägt keine Nutzerinformation, nur eine undurchsichtige Zeichenfolge, die der Server bei jeder Anfrage auflöst. Löschen Sie den Datensatz im Store, ist die Session bei der nächsten Anfrage verschwunden – auf jedem Gerät.
Was in einem JWT steckt
Ein JSON Web Token überspringt das Nachschlagen, indem es den Zustand direkt in das Token legt, signiert, damit es nicht manipuliert werden kann.
Ein JWT besteht aus drei base64url-kodierten Teilen, durch Punkte verbunden: header.payload.signature. Das Payload ist ein JSON-Objekt aus Claims. RFC 7519 definiert eine kurze Liste registrierter Claims:
| Claim | Name | Bedeutung |
|---|---|---|
iss | Issuer | Wer das Token ausgestellt hat |
sub | Subject | Auf wen sich das Token bezieht |
aud | Audience | Für wen das Token bestimmt ist |
exp | Expiration | Token nach diesem Zeitpunkt ablehnen |
nbf | Not before | Token vor diesem Zeitpunkt ablehnen |
iat | Issued at | Wann das Token erstellt wurde |
jti | JWT ID | Eindeutige ID, nützlich für Widerruf und Replay-Prüfungen |
Der Server signiert Header und Payload mit einem Secret (HMAC) oder einem privaten Schlüssel (RSA/ECDSA) und sendet das Ergebnis an den Client. Jeder Server, der den passenden Schlüssel besitzt – oder bei asymmetrischer Signatur nur den öffentlichen Schlüssel –, kann die Signatur prüfen und den Claims vertrauen. Kein Datenbank-Roundtrip, kein gemeinsamer Session-Store. In einem System mit mehreren Diensten kann jeder ein Token eigenständig prüfen.
Die Kehrseite: Wer das Token besitzt, kann das Payload lesen. Base64 ist eine Kodierung, keine Verschlüsselung. Packen Sie niemals ein Passwort, ein Secret oder personenbezogene Daten, die Sie nicht in ein Log schreiben würden, in das Payload eines JWT.
Wie Sie Session-Authentifizierung in Express einrichten
Eine typische Node-Konfiguration nutzt express-session als Middleware und connect-redis, damit Sessions einen Server-Neustart überstehen und über mehrere Instanzen funktionieren. Wenn Redis für Sie neu ist, deckt wie Redis-Caching funktioniert die Grundlagen ab, um es zum Laufen zu bringen.
| |
| |
req.session.destroy() entfernt den Eintrag in Redis. Die nächste Anfrage mit diesem Cookie findet nichts mehr und wird als abgemeldet behandelt. Keine Token-Blacklist, nichts, was später nachgerüstet werden muss.
Wie Sie JWT-Authentifizierung in Node einrichten
Derselbe Ablauf mit jsonwebtoken, mit einem kurzlebigen Access-Token und einem separaten Refresh-Token:
| |
| |
jwt.verify prüft die Signatur und die Claims exp/nbf in einem einzigen Aufruf und wirft eine Exception, wenn eines der beiden fehlschlägt. Das ist die gesamte Authentifizierungsprüfung, ohne dass ein Store beteiligt ist.
Die Option algorithms: ["HS256"] ist entscheidend. Ohne sie könnte ein Token, das mit einem anderen Algorithmus signiert wurde als vorgesehen, trotzdem durchkommen – genau so sind mehrere JWT-Bibliotheken bei „Alg Confusion"-Schwachstellen gelandet. Legen Sie die akzeptierte Algorithmenliste bei der Prüfung fest und lehnen Sie alg: "none" grundsätzlich ab.
Das Refresh-Token macht ein 15-minütiges Access-Token erst praktikabel. Der Client behält es und tauscht es, wenn das Access-Token abläuft, gegen ein neues Paar. Speichern Sie das jti des Refresh-Tokens serverseitig, damit Sie genau diese eine Kette widerrufen können, ohne bereits im Umlauf befindliche Access-Tokens anzufassen.
Warum JWTs schwer zu widerrufen sind
Eine Session ist ein Datensatz in einem Store: Löschen Sie ihn, ist der Nutzer überall abgemeldet. Ein JWT ist ein eigenständiger Claim, den jeder Prüfer akzeptiert, bis exp etwas anderes sagt. Abmelden, einen Nutzer sperren oder seine Rolle herabstufen ändert an einem bereits ausgestellten Token nichts, weil der prüfende Server keinen Datensatz hat, den er löschen könnte.
Die üblichen Gegenmaßnahmen:
- Kurze Lebensdauer des Access-Tokens. 15-30 Minuten ist der gängige Bereich, damit ein gestohlenes oder veraltetes Token nur ein kleines Zeitfenster hat.
- Rotation des Refresh-Tokens. Jeder Refresh stellt ein neues Refresh-Token aus und macht das alte ungültig; die Wiederverwendung eines alten Refresh-Tokens widerruft die gesamte Kette. OAuth 2.1 verlangt das (oder sender-gebundene Token) für öffentliche Clients.
- Eine eng gefasste Denylist. Um ein bestimmtes Token vor dem Ablauf zu töten, indizieren Sie die Denylist über
jtiplusiss, nicht über die rohe Token-Zeichenfolge. Das OWASP JWT Cheat Sheet erklärt, warum das Blacklisten der Zeichenfolge selbst unzuverlässig ist. Eine bei jeder Anfrage geprüfte Denylist bringt auch wieder einen Datenbank-Lookup bei jeder Anfrage mit sich – genau die Kosten, die JWTs eigentlich vermeiden sollten.
Wenn Ihre App am Ende ohnehin bei jeder Prüfung ein Nachschlagen macht, wäre eine Session das einfachere Design gewesen.
Wo Sie ein JWT im Browser speichern
Legen Sie in einer Browser-App das Token in ein HttpOnly-, Secure-Cookie, nicht in localStorage. Jedes Skript, das auf der Seite läuft, kann localStorage lesen, also liefert eine einzige XSS-Lücke jedes darin enthaltene Token aus. Die Kehrseite: Sobald das Token in einem Cookie reist, sendet der Browser es automatisch, und Sie müssen sich wieder mit SameSite und CSRF-Token gegen CSRF verteidigen – genau wie bei Sessions. Der vollständige Vergleich steht in LocalStorage, SessionStorage oder Cookies: Was wofür.
Wann Sessions und wann JWTs verwenden
| Situation | Verwenden Sie |
|---|---|
| Eine serverseitig gerenderte App, ein Backend | Sessions: einfacher, sofortiger Widerruf |
| Sofortiger Logout oder sofortige Rechteänderungen | Sessions, oder JWTs mit einer Denylist über jti |
| Mehrere Backend-Dienste, die den Nutzer jeweils eigenständig prüfen | JWTs: kein gemeinsamer Session-Store, den man ansprechen muss |
| Mobile App oder API-Clients von Drittanbietern | JWTs: Bearer-Token passen zu einer zustandslosen HTTP-API |
Keines der beiden ist im Abstrakten sicherer. Eine Redis-gestützte Session und ein 15-minütiges JWT mit rotierten Refresh-Tokens halten beide stand. Der Unterschied ist architektonisch: Wie viele Dienste müssen dem Claim vertrauen, und wie schnell müssen Sie ihn zurücknehmen können.
Bauen Sie eine App mit einem Backend? Wählen Sie standardmäßig Sessions und bekommen den Widerruf kostenlos dazu. Wechseln Sie zu JWTs an dem Tag, an dem ein zweiter Dienst wissen muss, wer der Nutzer ist, ohne den Dienst zurückzurufen, der ihn angemeldet hat.