4 Punkte von GN⁺ 18 시간 전 | 2 Kommentare | Auf WhatsApp teilen
  • JWT eignet sich nicht dafür, Nutzer eingeloggt zu halten; für diesen Zweck sind normale Cookie-Sessions besser geeignet
  • Die JWT-Spezifikation geht von kurzlebigen Tokens mit einer Laufzeit von etwa 5 Minuten oder weniger aus, während Sessions eine längere Lebensdauer benötigen
  • Sichere zustandslose Authentifizierung ist schwer zu erreichen, und um Tokens sicher zu handhaben, ist letztlich doch irgendeine Form von Zustandspeicher nötig
  • Ein JWT, das nur ein einfaches Session-Token enthält, ist im Vergleich zu normalen Session-Cookies ineffizienter und weniger flexibel, und Authentifizierungsdaten sollten nicht in localStorage oder sessionStorage gespeichert werden
  • Wenn kurzlebige signierte Tokens benötigt werden, ist PASETO, das für Sicherheit entworfen wurde, die bessere Wahl, sollte aber nicht für Sessions verwendet werden

Kurzfassung

  • JWT sollte nicht verwendet werden, um Nutzer eingeloggt zu halten; für diesen Zweck sind normale Cookie-Sessions das bessere Werkzeug
  • JWT wurde für diesen Zweck nicht entworfen und ist dafür nicht sicher, während reguläre Cookie-Sessions besser zur Aufrechterhaltung von Login-Sessions geeignet sind
  • Als verwandtes Thema gilt außerdem: Authentifizierungsnachweise einschließlich JWT-Tokens sollten nicht in localStorage oder sessionStorage gespeichert werden
  • Es gibt Präsentationen zum Thema JWT, aber andere Themen wie CSRF-Schutz werden meist nur kurz behandelt und sollten daher separat aus anderen Quellen gelernt werden
  • Auch die am Ende des Videos genannten „gültigen“ JWT-Anwendungsfälle lassen sich leicht mit besseren und sichereren Werkzeugen abdecken, konkret mit PASETO

Warum JWT vermieden werden sollte

  • Die JWT-Spezifikation wurde nur für sehr kurzlebige Tokens mit einer Laufzeit von etwa 5 Minuten oder weniger entworfen, während Sessions länger leben müssen
  • Zustandslose Authentifizierung auf sichere Weise ist nicht möglich; um Tokens sicher zu verarbeiten, ist zwingend irgendein Zustand erforderlich
    • Wenn ohnehin ein Datenspeicher benötigt wird, ist es besser, alle Daten zu speichern, statt nur einen Teil des Token-Zustands zu verwalten
    • Näher behandelt wird das unter http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
    • Es gibt tatsächlich Anwendungen, die JWT auf diese Weise einsetzen, aber solche Anwendungen sind fehlerhaft, daher sollte derselbe Fehler nicht wiederholt werden
  • Ein JWT, das nur ein einfaches Session-Token speichert, ist im Vergleich zu normalen Session-Cookies ineffizienter und weniger flexibel und bringt keine zusätzlichen Vorteile
  • Der JWT-Standard selbst genießt unter Sicherheitsexperten kein Vertrauen und sollte daher generell für Sicherheits- und Authentifizierungszwecke ausgeschlossen werden

Einwände

  • Der Einwand „Auch Google verwendet JWT“ bezieht sich nicht auf Browser-Nutzersessions
    • Google verwendet für Browser-Nutzersessions kein JWT, sondern normale Cookie-Sessions
    • JWT wird nur als Übertragungsmechanismus für Single Sign On verwendet, um die Login-Session eines Servers oder Hosts an einen anderen Server oder Host weiterzugeben
    • Diese Nutzungsweise fällt in den Bereich sinnvoller JWT-Anwendungsfälle
    • Google verfügt über die Ressourcen und Sicherheitsexperten, um eine sicherere JWT-Implementierung zu entwickeln und zu pflegen
    • Googles JWT ist faktisch nicht dasselbe wie JWT anderswo
  • Der Einwand „Zustandslos ist besser“ passt nicht zu den Anforderungen sicherer Authentifizierung
    • Ohne enorme Ressourcen lässt sich echte zustandslose Authentifizierung nicht sicher betreiben
    • Siehe dazu auch Stateless is a lie
  • Das Problem „Ich weiß nicht, wie man Sessions einrichtet“ lässt sich in den meisten Fällen durch die Dokumentation und Implementierungen von Webserver-Frameworks lösen
    • Session-Technik ist nicht besonders neu, deshalb sieht man seltener Artikel, die Sessions erklären
    • Mit der Dokumentation der Session-Implementierung sollte sich der Einrichtungsprozess nachvollziehen lassen
    • Fast jedes Webserver-Framework enthält eine Session-Implementierung, und selbst wenn sie nicht standardmäßig aktiviert ist, lässt sie sich normalerweise leicht aktivieren
    • Express und andere Node.js-Frameworks sind wegen ihrer hohen Modularität und ihres Single-Purpose-Charakters teilweise eine Ausnahme
    • In Express reicht es, die Middleware express-session und einen zum Speicher passenden Store-Connector zu verwenden
    • Empfohlen wird die Verwendung von connect-session-knex mit Postgres, MySQL oder möglichst SQLite

Kurzlebige Tokens

  • Wenn für einen Zweck kurzlebige signierte Tokens benötigt werden, gibt es mit PASETO eine bessere Spezifikation, die mit Blick auf Sicherheit entwickelt wurde
  • Auch mit PASETO sollte man es nicht für Sessions verwenden

Wie Sessions funktionieren

  • Wer mehr darüber lernen möchte, wie Sessions funktionieren, sollte sich joepie91s gist ansehen

2 Kommentare

 
shj5508 9 시간 전
  1. JWT dient dazu, Tokens zu verschlüsseln und DB-Abfragen zu reduzieren; es ist kein Konzept, das im Gegensatz zur Cookie-Authentifizierung steht. Wenn man JWT in einem sicheren Cookie speichert, ist das Risiko eines Diebstahls dasselbe wie bei der Legacy-Cookie-Authentifizierung.

  2. Das Verwalten einer Ablauf-Liste, um JWTs ablaufen zu lassen, bietet aus Performance-Sicht Vorteile. Es gibt einen Kostenunterschied zwischen dem Abfragen nur der Ablauf-Informationen in Redis und dem Ausführen von DB-Abfragen über alle Benutzer.

Zehntausende indexbasierte Abfragen über 100.000 Mitgliedszeilen (Legacy-Cookie-Methode)
vs
Zehntausende Abfragen von 50 Einträgen in der Ablauf-Liste in Redis (sofortiges JWT-Expiry)

JWT hat durchaus Vorteile. In kleineren Umgebungen fällt der Unterschied nur weniger stark ins Gewicht.

 
Hacker-News-Kommentare
  • Es fehlt ein wichtiger Hinweis: Es geht um browserbasierte Benutzersitzungen
    Für die Kommunikation zwischen Services gibt es viele Fälle, in denen sich JWT gut einsetzen lässt
    Nebenbei: Ich habe einen Teil des verlinkten Artikels gelesen, darunter z. B. https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba.... Wenn JWT wirklich ein so furchtbar unsicherer Standard ist, dann veröffentlicht doch bitte eine Methode, um AWS STS AssumeRoleWithWebIdentity zu hacken – oder veröffentlicht sie nicht und startet stattdessen auf den produktiven AWS-Konten von Fortune-500-Unternehmen einfach Crypto-Miner. Wenn JWT wirklich so unsicher ist, sagt bitte Bescheid, wenn ihr Erfolg habt /sarkasmus

    • Das ist genau die vernünftige Schlussfolgerung. Ich stimme zu, dass JWT für Benutzersitzungen im Browser das falsche Werkzeug ist
      Der Signatur-/Verschlüsselungsteil von JWT ist komplex, und auch gängige JWT-Bibliotheken sind erst inzwischen größtenteils zur Vernunft gekommen; früher war das nicht so. Viele Bibliotheken akzeptierten den "none"-Algorithmus [1], und in manchen Fällen wurde ein öffentlicher Schlüssel wie ein Shared Secret verwendet, sodass Angreifer Tokens fälschen konnten [2]. Das ist genau das Resultat der Komplexität, die der verlinkte Artikel kritisiert
      JWT kann außerdem Funktionen, die man bei Benutzersitzungen möchte, teils gar nicht leisten. Ohne irgendwo eine Sperrliste zu führen, lässt es sich nicht ungültig machen. Wenn man aber bei jeder Anfrage eine Kennung gegen eine Sperrliste prüfen muss, kann man genauso gut eine undurchsichtige Session-ID verwenden und bei jeder Anfrage nachschlagen. Natürlich kann man kurzlebige Tokens verwenden und ständig erneuern, aber in gewöhnlichen Anwendungen, die ohnehin State halten müssen, gibt es wenig Grund dafür
      Allerdings stimme ich voll zu, dass signierte Tokens in verteilten Systemen oder bei Kommunikation zwischen Maschinen nützlich sein können. Man sollte die beiden Fälle nicht verwechseln
      [1] https://nvd.nist.gov/vuln/detail/cve-2022-23540
      [2] https://nvd.nist.gov/vuln/detail/CVE-2024-54150
    • Früher gab es bei JWT viele Probleme wegen Bibliotheken mit schlechten Defaults. Vor ein paar Jahren waren auch Downgrade-Angriffe ziemlich verbreitet
      Inzwischen haben die wichtigen Bibliotheken in mehreren Sprachen vernünftigere Defaults, daher würde ich sagen, dass es heute in der Praxis ziemlich sicher geworden ist
    • JOSE kann selbst bei korrekter Implementierung noch problematisch sein. Die zugehörige API-Oberfläche ist oft nicht besonders gut
      Wenn „sicher, sofern man es richtig anfasst und benutzt“ automatisch gutes Design bedeuten würde, dann würde dasselbe auch für X.509 gelten
      In vielen Fällen gibt es bessere Alternativen. Standard-Session-Tokens oder API-Keys werden auf den meisten großen Websites breit eingesetzt und passen für die meisten Anwendungsfälle fast perfekt
      Das soll nicht heißen, dass solche Standards keinerlei Wert haben. Das Beste daran ist wohl, dass es ein grundlegender Standard ist, um Dinge ohne ASN.1-Encoding hin und her zu schicken, und die ASN.1-Werkzeuge wirken extrem fragil und fehleranfällig
    • Dem ersten Teil stimme ich zu, aber der Zusatz ist ein logischer Fehlschluss. Etwas muss nicht tatsächlich gehackt werden können, damit man sagen darf, dass es unsicher ist
      Ich weiß zum Beispiel nicht, wie man SAML konkret ausnutzt, aber ich weiß, dass es ein schrecklicher Standard ist, weil es den gesamten XML-Parser zur Angriffsfläche macht. Ich bin kein Sicherheitsforscher und weiß nicht, wie man Schwachstellen in XML-Parsern findet, aber ich kann trotzdem erkennen, dass eine große Angriffsfläche schlecht ist
    • Die Abstammungslinie von Schwachstellen, die in realen Anwendungen durch JWT entstanden sind, ist lang
  • JWT soll unsicher sein, selbst wenn man ein vertrauenswürdiges RSA-/Public-Key-basiertes Signaturverfahren verwendet? Also auch ohne Shared Secret?
    Auch die Behauptung, JWT lebe zu lange, wirkt seltsam. Man kann die Lebensdauer von JWT begrenzen und ein Erneuerungsmodell gegenüber der Autorisierungsinstanz haben. Selbst bei Cookie-basierten Sessions wird am Ende doch irgendwo etwas gespeichert. Ein JWT kann man nur 5–15 Minuten gültig machen, und 15 Minuten liegen in der Größenordnung der Cache-Dauer vieler Autorisierungssysteme, einschließlich Entra. Auch 5-Minuten-Tokens können mit einem Erneuerungssystem im Browser gut funktionieren
    Außerdem bevorzuge ich es, Identität/Authentifizierung von Anwendungs- und API-Services zu trennen. Man kann den Kontext externalisieren, und JWT pro Anfrage zu verarbeiten ist oft leichter zu handhaben als gemeinsam genutzte Cache-/State-Systeme, die sporadisch ausfallen können. Bei signierten Tokens kann man die Signatur gegenüber einer bekannten Instanz verifizieren

    • JWT kann so erstellt werden, dass es in 30 Sekunden oder sogar in 1 Sekunde ungültig wird. Beim Erzeugen eines JWT sollte man die audience setzen
      Davon abgesehen ist die Signatur kryptografisch gültig. Man kann einfach alle JWT jedes Mal mit kurzer Lebensdauer verifizieren
      Zur Einordnung: OIDC-Tokens sind alle JWT
  • Wenn man Sessions mit JWT-Sperrlisten vergleicht, gibt es auch eine Logik zugunsten von JWT-Sperrlisten. JWT haben eine begrenzte Ablaufzeit, also muss man eine Sperrliste nur für Tokens pflegen, die noch nicht abgelaufen sind
    Wahrscheinlich ist nur ein kleiner Teil der noch im Umlauf befindlichen gültigen JWT gesperrt, also fragt man pro Request nur einen sehr kleinen Datensatz ab
    Bei Sessions ist die Liste gültiger Sessions wahrscheinlich um mehrere Größenordnungen größer als eine Sperrliste, daher sind die Lookup- und Speicherkosten durch Statefulness höher
    Außerdem heißt es im Artikel, JWT seien zustandslos, aber meist sind sie das nicht. In der Regel validiert man nicht nur das JWT, sondern lädt bei jeder Anfrage auch das passende Identitätsobjekt, also Benutzerdetails, um zu prüfen, ob der Benutzer noch aktiv ist und die betreffende Aktion noch ausführen darf. Man kann pro Benutzer eine Sperrliste oder einen Wert wie minimum_issued_at verwenden, um das iat-Feld des JWT zu validieren. Damit ist auch das Muster „auf allen Geräten abmelden“ möglich: Diese Aktion setzt einfach minimum_issued_at des Benutzers auf $NOW, und alle vorherigen Tokens werden damit ungültig. Eine Abfrage einer individuellen Sperrliste ist nicht nötig

    • In dem Moment, in dem man das Benutzerobjekt nachladen muss, ist der Kernvorteil von JWT verschwunden, und man kann es auch einfach weglassen
    • Das Nachladen von Session-Daten ist ein select mit Index in der Datenbank und gibt 0–1 Zeilen zurück. In den meisten Fällen ist das kein Problem
    • Sessions haben ebenfalls eine Ablaufzeit, und man kann sie beliebig setzen
  • Dieser Artikel verlinkt für den Großteil des „Warum“ auf andere Blogbeiträge, und diese Blogbeiträge scheinen sich meist darüber zu beschweren, dass man „einzelne JWT-Token nicht ungültig machen kann“
    Als ich das jeweils implementiert habe, war die übliche Vorgabe, irgendwo auf ungültig gemachte Nonces zu prüfen, und damit wäre auch das zweite Argument in diesem Beitrag gelöst
    Die Aussage „Die JWT-Spezifikation selbst wird von Sicherheitsexperten nicht vertraut“ scheint mehr Belege zu brauchen als einen einzelnen Blogpost. Und dieser Beitrag scheint größtenteils schlechte Implementierungen zu kritisieren, aber Probleme durch schlechte Implementierungen gibt es bei jedem Standard
    Insgesamt weiß ich nicht, was ich erwartet habe, als ich auf einen beliebigen gist-Link geklickt habe

    • Einige frühe Implementierungen erlaubten es, im Header beliebige Aussteller zu setzen und diesen dann einfach zu vertrauen, aber das war natürlich von Anfang an falsch. Wenn man nur vertrauenswürdige oder „bekannte“ Aussteller zulässt, verschwinden viele dieser kontextabhängigen Bedenken
      Außerdem kann man im Browser durchaus JWTs mit kürzerer Lebensdauer verwenden und den Agenten sie selbst erneuern lassen. Mit Azure Entra oder diversen anderen Anbietern funktioniert das in der Praxis genau so. Man kann JWTs relativ kurz halten, etwa 5–15 Minuten, und zusätzlich prüfen, ob die jti verworfen wurde
      JWTs sind sehr nützlich, um die Autorisierungsinstanz vom Anwendungs-/API-System zu trennen und wiederzuverwenden. Man verlagert die Angriffsfläche, aber auf eine vertrauenswürdige Weise. Weltweit wird an vielen Stellen Public-Key-Kryptografie verwendet, einschließlich SSH. Gemeinsame Geheimnisse oder langlebige Token würde ich nicht verwenden, aber kurzlebige, mit Public Keys signierte Token aus einer verifizierten und bekannten Quelle sind im Allgemeinen in Ordnung
      Eher sind in der Praxis oft API-Schlüssel das eigentliche Problem. Ich musste das gerade erst implementieren; in meinem Fall habe ich API-Schlüssel ebenfalls wie Bearer-Token aussehen lassen, mit einem kurzen sak.-Präfix, gefolgt von einem Identitätsteil (base64url-UUID-Bytes) und danach einem Geheimwert (base64url-Bytes). In der Datenbank speichere ich die UUID und einen salt+hash auf Passphrasen-Niveau, der aus dem Geheimwert erzeugt wird. Daher muss ein erzeugter API-Schlüssel als Geheimnis behandelt werden, und in der Datenbank wird er nur in eine Richtung gespeichert, sodass eine Datenbankkompromittierung nicht automatisch zu einer Kompromittierung der Authentifizierung führt
      Trotzdem ist ein API-Key-Leak sehr viel wahrscheinlicher als ein Problem mit einer gut implementierten JWT-Lösung
    • Der Behauptung „einzelne JWT-Token kann man nicht ungültig machen“ stimme ich nicht zu 100 % zu. Bei Implementierungen irgendwo auf ungültig gemachte Nonces zu prüfen, ist für mich gesunder Menschenverstand, und ich bin jedes Mal wieder überrascht, wenn ich feststelle, dass Leute das nicht tun
  • Ich bin zufällig auf diesen Artikel gestoßen und fand es interessant, dass das Thema gerade wieder aktuell ist, weil ich früher viel dazu gearbeitet habe. Dann habe ich geklickt und festgestellt, dass der Autor auf einen Teil meines Materials verlinkt hatte. Da kamen wirklich alte Erinnerungen hoch
    Wie auch immer, viel klügere Leute als ich haben dieses Thema über Jahre hinweg breit behandelt, aber ich denke auch 2026 noch, dass JWT für Web-Authentifizierung das falsche Werkzeug ist. Für Service-zu-Service-Zwecke ist es in Ordnung, aber wenn man die Wahl hat, sollte man einfach PASETO verwenden. Das löst viele Probleme

  • Ich bin gerade dabei, RabbitMQ für Benachrichtigungs-Push auf einer Website anzubinden. Ich verwende dabei JWT-Authentifizierung, um zu steuern, was der Client wo lesen darf, mit kurzer Lebensdauer und regelmäßiger Token-Erneuerung
    Ich kenne nicht viele andere Setups, die in ihrer Einfachheit daran herankommen. Man fügt einfach einen Endpoint hinzu, der für gültige Sitzungen JWT-Token ausgibt, und schon ist es erledigt; auch benutzerspezifische Berechtigungen sind möglich

  • Einer der verlinkten Artikel, die erklären sollen, warum man JWT nicht verwenden sollte, ist selbst in wohlwollender Lesart merkwürdig
    https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...
    Zusammengefasst lautet die Aussage: „Einige Bibliotheken hatten Bugs“, und dann wird empfohlen, libsodium dazuzunehmen und es selbst zu machen. Das ist ein derart absurder Ratschlag, dass man ihn kaum ernst nehmen kann. Jede Software hat Bugs. Bei Heartbleed stand das ganze Internet in Flammen, und trotzdem verwenden wir weiterhin TLS und OpenSSL
    Die Aussage „Die JWT-Spezifikation wurde speziell nur für Token mit sehr kurzer Lebensdauer entworfen, ungefähr 5 Minuten oder weniger“ höre ich zum ersten Mal, und ich kann auch keine Belege dafür finden. In RFC 7519 steht so etwas nicht

  • Üblicherweise verwende ich JWT als eine Art Authentifizierungs-Cache. Man erhält ein Authentifizierungs-Token von einem Authentifizierungsdienst, und dieses Token erteilt dann Berechtigungen gegenüber anderen Diensten
    Das hat mehrere Vorteile, aber der Kern ist, dass nachgelagerte Dienste weder mit der Authentifizierungsdatenbank interagieren müssen noch die Berechtigung haben müssen, selbst Token auszustellen. Vorausgesetzt, man verwendet RS256 und nicht HMAC. Wenn also ein nachgelagerter Dienst kompromittiert wird, ist das nicht annähernd so verheerend wie die Kompromittierung eines Dienstes mit Zugriff auf die Authentifizierungsdatenbank
    Wenn sich sensible Daten im Token befinden, muss man JWE verwenden, aber das ist nicht besonders gut, weil man dann bei jeder Nutzung einen internen Dienst mit dem privaten Schlüssel zum Entschlüsseln des Tokens anfragen muss
    Eine Struktur, die ich häufig verwende, ist {"id": (uuid), "scopes": ["scope:read/write"]}
    Auch für SPAs ist das ziemlich gut. Der statische Site-Server kann nämlich JWE mit einem Public Key prüfen, bevor er Ressourcen ausliefert. Meine Methode ist, statische Sites in der Form /(scope)/path zu kompilieren, sodass der statische Dienst Seiten, auf die kein Zugriff bestehen darf, gar nicht erst ausliefert. Das ist sehr nützlich, wenn man dem Benutzer keine Funktionen zeigen will, die nur das Backend hat, wie etwa Admin-Panels, oder keine angreifbaren internen Service-Pfade offenlegen möchte
    Die JWT-Lebensdauer für den „Backend-Zugriff“ beträgt etwa 5 Minuten, und Dinge wie /me werden in localStorage zwischengespeichert, es sei denn, /refresh weist explizit an, den localStorage-Cache zu verwerfen. Der Request-Handler der SPA-Anwendung erkennt dann „Erneuerung erforderlich“ und erneuert das Token
    Ich denke, ein Großteil dieser Verantwortung liegt bei node/next- und Python-Bibliotheken. Das Backend schreibe ich in stark typisierten Sprachen, und das Frontend besteht immer aus vorab kompilierten statischen Seiten. Mein aktuelles Frontend-Setup nutzt VITE, mit einer vorgerenderten Landingpage und der Anwendung als normaler SPA
    Selbst unter Berücksichtigung all dessen stimme ich diesem gesamten gist entschieden nicht zu. JWT kann so sicher gemacht werden, wie man es braucht

  • JWT ist in Ordnung, und der Titel wirkt etwas reißerisch
    Stattdessen gäbe es gute Themen, über die man sprechen könnte: wann man verschlüsselte Werte (symmetrisch oder asymmetrisch), zufällige aber geheime Werte oder signierte Werte verwendet (lesbar, aber nicht manipulierbar), wo man solche Werte speichert (Arbeitsspeicher, localStorage, Cookies), wie man verhindert, dass sie ewig gültig bleiben, und ob man sie schon vor ihrem natürlichen Ablaufzeitpunkt widerrufen können muss