Hallo zusammen,
Ich beschäftige mich gerade mit JSON Web Token (JWT) in Webapplikationen (mit PHP, spielt hier aber keine Rolle), komme beim Konzept selber aber nicht weiter. Ich freue mich auf eure Meinungen
Nachfolgend mal was ich weiss oder zu wissen glaube und welche Ansätze aber auch Probleme es gibt. Korrigiert mich, wo ich falsch liege.
1. Ziel von JWT
Grundsätzlich könnte man ja allbekannte Sessions - ein Token in der Datenbank und auf dem Client - einsetzen.
Durch den Einsatz von JWT sollen Probleme beim Load Balancing verhindert werden (Sync-Probleme) und es muss nicht mehr bei jedem Request auf die DB zugegriffen werden (zumindest nicht für die Authentifizierung).
2. Grundsätzlicher Workflow
Die Umsetzung erfolgt mittels einem Access Token (AT) und einem Refresh Token (RT). Der Access Token ist nur eine kurze Zeit gültig, der Refresh Token länger (oder ewig). Läuft der Access Token ab, wird er mit dem Refresh Token erneuert.
Grundsätzlich läuft der Prozess dann wie folgt ab:
1. Der User loggt sich mit User + PW ein
2. Der Server generiert einen Refresh und Access Token und sendet diese an den Client
3. Der User arbeitet mit der Web-App und sendet somit Anfragen an den Server, zusammen mit dem Access Token
4. Der Server prüft ob der Access Token gültig ist (mit dem Secret Key) und ob der nicht abgelaufen ist
5. Solange der Access Token gültig ist, können die Anfragen direkt beantwortet werden, ohne auf die DB zugreifen zu müssen.
6. Nach einiger Zeit macht der User eine Abfrage mit einem abgelaufenen Access Token, der Server merkt das und unterbindet die Anfrage
7. Der Client bittet mit dem noch gültigen Refresh Token um einen neuen Access Token und erhält diesen.
8. Der Client kann (ohne Unterbruch) direkt mit dem neuen Access Token weiterarbeiten.
FESTSTELLUNG 1: Gültigkeitsdauer des Access Token
Ein sehr kurzlebiger Access Token, z.B. eine Minute oder weniger, würde zu wesentlich mehr HTTP-Requests führen (Request mit falschem AT, Request für neuen AT, Request mit neuem AT).
Insofern muss der Access Code zum Beispiel eine Viertelstunde oder noch besser eine Stunde gültig sein. Nur jede Stunde mehr Anfragen zu haben, sollte vertretbar sein (gegenüber jede Minute).
FESTSTELLUNG 2: Kein Zugriff auf DB bei Access Token
Ein Access Token muss durch sich selbst validiert werden können und somit ohne Datenbankzugriff auskommen, ansonsten hat man ein Ziel von JWT nicht erreicht.
PROBLEM 1: Logout verhindern wenn User am Arbeiten ist
Wenn der Access Token abläuft, kann dieser mit dem Refresh Token erneuert werden - soweit kein Problem. Wenn hingegen der Refresh Token ausläuft, muss sich der User neu einloggen.
Nehmen wir an, der Access Token hat eine Laufzeit von 60 Minuten und der Refresh Token von einer Woche. 55 Minuten vor Ablauf des Refresh Tokens beginnt der User zu arbeiten.
Der User kann mit dem RT einen neuen AT für eine Stunde generieren. Nach einer Stunde ist der AT abgelaufen und weil mittlerweile auch der RT abgelaufen ist, wird er einfach ausgeloggt.
Aus UX-Sicht sehr suboptimal, da er gerade am arbeiten war.
Wie kann dies einfach und sauber gelöst werden?
PROBLEM 2: Von allen Geräten abmelden
Aus Sicherheitsgründen möchte sich der User von allen Geräten abmelden (eine Funktion die viele Dienste anbieten). Nehmen wir an, ein Angreifer gelang in Besitz des AT (und vielleicht auch RT) des Users.
Da der Access Token jedoch ohne DB-Zugriff validiert wird, kann der Server den Zugriff nicht direkt verhindern. In diesem Fall den AT auf eine Blacklist zu setzen, hilft nur dann, wenn wir die Blacklist bei jedem Aufruf prüfen.
Damit verstossen wir jedoch gegen das Ziel, nicht auf die Datenbank zugreifen zu wollen. Somit kann der Angreifer 60 Minuten lang auf den Account zugreifen.
Um zu verhindern, dass ein Angreifer neue AT generieren kann, könnte man für die RT eine Blacklist einführen. Dadurch muss zwar auch auf die DB zugegriffen werden, allerdings nur alle 60 Minuten.
Somit kann verhindert werden, dass der Angreifer länger als 60 Minuten auf den Account zugreifen kann - was aber eben auch schon zu lange ist.
Wie kann eine "Von allen Geräten abmelden"-Funktion angeboten werden (die quasi sofort wirkt), ohne jedes mal auf eine DB zugreifen zu müssen?
PROBLEM 3: Logout verhindern (Fortsetzung Problem 1)
Um zu verhindern, dass der User automatisch ausgeloggt wird, müsste er einen neuen Refresh Token haben, damit er wieder Access Tokens beantragen kann.
Ohne die Logindaten erneut einzugeben, müsste es also möglich sein, mit einem bestehenden (gültigen) RT einen neuen RT zu beantragen, der dann wieder eine Woche (oder auch kürzer) gültig ist.
Was ist aber nun, wenn ein Angreifer meinen Refresh Token geklaut hat und damit einen neuen Refresh Token generiert?
Wenn ich eine Blacklist führe, kenne ich den neuen Token des Angreifers nicht und kann ich ihn somit nicht sperren. Daher müsste ich eigentlich pro Login eine ID speichern und im RT hinterlegen. Der neue RT erhält dann dieselbe Login ID.
Ich als User sperre dann die Login ID und er kann somit keine neuen Refresh Tokens generieren. Dann habe ich aber eher eine Whitelist als eine Blacklist.
Also kann ich ja gleich eine Whitelist führen und somit all meine Refresh Tokens abspeichern. Wenn der Angreifer nun einen neuen RT beantragt (mit dem geklauten), kann ich beide RT sperren.
Beim Load Balancing könnte dies aber wieder zu einem Problem führen, wenn der Angreifer weiterhin neue Tokens generieren kann.
Ich könnte ohne Probleme nochmals so viel Text schreiben, aber ich denke ihr habt genug Und ich lasse euch mal zu Wort kommen.
Gruss
Sandro
Ich beschäftige mich gerade mit JSON Web Token (JWT) in Webapplikationen (mit PHP, spielt hier aber keine Rolle), komme beim Konzept selber aber nicht weiter. Ich freue mich auf eure Meinungen
Nachfolgend mal was ich weiss oder zu wissen glaube und welche Ansätze aber auch Probleme es gibt. Korrigiert mich, wo ich falsch liege.
1. Ziel von JWT
Grundsätzlich könnte man ja allbekannte Sessions - ein Token in der Datenbank und auf dem Client - einsetzen.
Durch den Einsatz von JWT sollen Probleme beim Load Balancing verhindert werden (Sync-Probleme) und es muss nicht mehr bei jedem Request auf die DB zugegriffen werden (zumindest nicht für die Authentifizierung).
2. Grundsätzlicher Workflow
Die Umsetzung erfolgt mittels einem Access Token (AT) und einem Refresh Token (RT). Der Access Token ist nur eine kurze Zeit gültig, der Refresh Token länger (oder ewig). Läuft der Access Token ab, wird er mit dem Refresh Token erneuert.
Grundsätzlich läuft der Prozess dann wie folgt ab:
1. Der User loggt sich mit User + PW ein
2. Der Server generiert einen Refresh und Access Token und sendet diese an den Client
3. Der User arbeitet mit der Web-App und sendet somit Anfragen an den Server, zusammen mit dem Access Token
4. Der Server prüft ob der Access Token gültig ist (mit dem Secret Key) und ob der nicht abgelaufen ist
5. Solange der Access Token gültig ist, können die Anfragen direkt beantwortet werden, ohne auf die DB zugreifen zu müssen.
6. Nach einiger Zeit macht der User eine Abfrage mit einem abgelaufenen Access Token, der Server merkt das und unterbindet die Anfrage
7. Der Client bittet mit dem noch gültigen Refresh Token um einen neuen Access Token und erhält diesen.
8. Der Client kann (ohne Unterbruch) direkt mit dem neuen Access Token weiterarbeiten.
FESTSTELLUNG 1: Gültigkeitsdauer des Access Token
Ein sehr kurzlebiger Access Token, z.B. eine Minute oder weniger, würde zu wesentlich mehr HTTP-Requests führen (Request mit falschem AT, Request für neuen AT, Request mit neuem AT).
Insofern muss der Access Code zum Beispiel eine Viertelstunde oder noch besser eine Stunde gültig sein. Nur jede Stunde mehr Anfragen zu haben, sollte vertretbar sein (gegenüber jede Minute).
FESTSTELLUNG 2: Kein Zugriff auf DB bei Access Token
Ein Access Token muss durch sich selbst validiert werden können und somit ohne Datenbankzugriff auskommen, ansonsten hat man ein Ziel von JWT nicht erreicht.
PROBLEM 1: Logout verhindern wenn User am Arbeiten ist
Wenn der Access Token abläuft, kann dieser mit dem Refresh Token erneuert werden - soweit kein Problem. Wenn hingegen der Refresh Token ausläuft, muss sich der User neu einloggen.
Nehmen wir an, der Access Token hat eine Laufzeit von 60 Minuten und der Refresh Token von einer Woche. 55 Minuten vor Ablauf des Refresh Tokens beginnt der User zu arbeiten.
Der User kann mit dem RT einen neuen AT für eine Stunde generieren. Nach einer Stunde ist der AT abgelaufen und weil mittlerweile auch der RT abgelaufen ist, wird er einfach ausgeloggt.
Aus UX-Sicht sehr suboptimal, da er gerade am arbeiten war.
Wie kann dies einfach und sauber gelöst werden?
PROBLEM 2: Von allen Geräten abmelden
Aus Sicherheitsgründen möchte sich der User von allen Geräten abmelden (eine Funktion die viele Dienste anbieten). Nehmen wir an, ein Angreifer gelang in Besitz des AT (und vielleicht auch RT) des Users.
Da der Access Token jedoch ohne DB-Zugriff validiert wird, kann der Server den Zugriff nicht direkt verhindern. In diesem Fall den AT auf eine Blacklist zu setzen, hilft nur dann, wenn wir die Blacklist bei jedem Aufruf prüfen.
Damit verstossen wir jedoch gegen das Ziel, nicht auf die Datenbank zugreifen zu wollen. Somit kann der Angreifer 60 Minuten lang auf den Account zugreifen.
Um zu verhindern, dass ein Angreifer neue AT generieren kann, könnte man für die RT eine Blacklist einführen. Dadurch muss zwar auch auf die DB zugegriffen werden, allerdings nur alle 60 Minuten.
Somit kann verhindert werden, dass der Angreifer länger als 60 Minuten auf den Account zugreifen kann - was aber eben auch schon zu lange ist.
Wie kann eine "Von allen Geräten abmelden"-Funktion angeboten werden (die quasi sofort wirkt), ohne jedes mal auf eine DB zugreifen zu müssen?
PROBLEM 3: Logout verhindern (Fortsetzung Problem 1)
Um zu verhindern, dass der User automatisch ausgeloggt wird, müsste er einen neuen Refresh Token haben, damit er wieder Access Tokens beantragen kann.
Ohne die Logindaten erneut einzugeben, müsste es also möglich sein, mit einem bestehenden (gültigen) RT einen neuen RT zu beantragen, der dann wieder eine Woche (oder auch kürzer) gültig ist.
Was ist aber nun, wenn ein Angreifer meinen Refresh Token geklaut hat und damit einen neuen Refresh Token generiert?
Wenn ich eine Blacklist führe, kenne ich den neuen Token des Angreifers nicht und kann ich ihn somit nicht sperren. Daher müsste ich eigentlich pro Login eine ID speichern und im RT hinterlegen. Der neue RT erhält dann dieselbe Login ID.
Ich als User sperre dann die Login ID und er kann somit keine neuen Refresh Tokens generieren. Dann habe ich aber eher eine Whitelist als eine Blacklist.
Also kann ich ja gleich eine Whitelist führen und somit all meine Refresh Tokens abspeichern. Wenn der Angreifer nun einen neuen RT beantragt (mit dem geklauten), kann ich beide RT sperren.
Beim Load Balancing könnte dies aber wieder zu einem Problem führen, wenn der Angreifer weiterhin neue Tokens generieren kann.
Ich könnte ohne Probleme nochmals so viel Text schreiben, aber ich denke ihr habt genug Und ich lasse euch mal zu Wort kommen.
Gruss
Sandro