Jak unieważnić token sesji przy wylogowaniu?

0

Hej, chcę dodać do projektu możliwość wylogowania co ma prowadzić do unieważnienia tokenu. Niestety nie mogę znaleźć sensownego poradnika jak to dobrze zrobić. Jaki jest najlepszy sposób żeby to zrobić? Próbowałem tego:
https://www.baeldung.com/spring-security-oauth-revoke-tokens
no i jeszcze próbowałem dodawać logout do konfiguracji spring security i mimo, że był sukces to token nadal działa.
Jakieś rady co do tego?

4

Można w takiej sytuacji np. nie używać tokenów, które nie były zaprojektowane do zastosowań, gdzie wymagana jest "sesja" użytkownika i możliwość jej zakończenia :)

No albo obudować tokeny dodatkowymi mechanizmami sesyjnymi.

0

Słabo, że nie da się po prostu jakoś skrócić długości życia tego tokenu, że po wylogowaniu wygasa od razu.
Jednak jest niby coś takiego jak podałem link revoke token ale za cholere to nie działa z moją konfiguracją.
Rozumiem, że w takiej pełnej aplikacji to nie ma znaczenia bo użytkownik nie ma dostępu do tokenu wiec nie trzeba go wygaszać i po prostu przenosi go do strony logowania?

7

JWT ma swoje wady i zalety, jedną z wad jest to, że ponieważ nie jest na serwerze (bo był zaprojektowany żeby wspierać server-less), to siłą rzeczy Ty z serwera nie możesz nic z tym zrobić.

Najlepszym wyjściem byłoby zrobić tak jak mówi @some_ONE, czyli skorzystać z tokenu sesji.

Wyjściem alternatywnym (które jest w zasadzie obejściem), jest dać krótki czas życia tokenu, np. na godzinę. Wtedy jak ktoś się wyloguje, to będzie zalogowany jeszcze tylko max. godzinę. Ale to jest obejście, a nie prawdziwe rozwiązanie.

W skrócie - z JWT się nie da zrobić tego co chcesz.

0

A nie można trzymać tokena w ciasteczkach i w sytuacji, kiedy użytkownik kliknie logout, zmodyfikować ciasteczko i usunąć z niego token ?

3

Można, ale jeśli chodzi o unieważnienie sesji to niczym to się nie różni od trzymania tokenu w localStorage/sessionStorage/pamięci i usunięcia go po stronie klienta jak kliknie logout.

W prostych przypadkach to może być wystarczające, ale jeśli to jakaś poważna aplikacja to pierwszy lepszy audyt bezpieczeństwa to wskaże jako krytyczną podatność.

1
Ornstein napisał(a):

A nie można trzymać tokena w ciasteczkach i w sytuacji, kiedy użytkownik kliknie logout, zmodyfikować ciasteczko i usunąć z niego token ?

A co jak ktoś ukradnie token, to wtedy do końca życia ma dostęp do konta i nigdy go nie wykopiesz z aplikacji.
Więc ważne jest dodać go do bazy zablokowanych tokenów i usunąć dopiero jak wygaśnie

0
Autysta napisał(a):
Ornstein napisał(a):

A nie można trzymać tokena w ciasteczkach i w sytuacji, kiedy użytkownik kliknie logout, zmodyfikować ciasteczko i usunąć z niego token ?

A co jak ktoś ukradnie token, to wtedy do końca życia ma dostęp do konta i nigdy go nie wykopiesz z aplikacji.
Więc ważne jest dodać go do bazy zablokowanych tokenów i usunąć dopiero jak wygaśnie

To zablokowane tokeny miałbym trzymać w bazie danych ?

1
Ornstein napisał(a):
Autysta napisał(a):
Ornstein napisał(a):

A nie można trzymać tokena w ciasteczkach i w sytuacji, kiedy użytkownik kliknie logout, zmodyfikować ciasteczko i usunąć z niego token ?

A co jak ktoś ukradnie token, to wtedy do końca życia ma dostęp do konta i nigdy go nie wykopiesz z aplikacji.
Więc ważne jest dodać go do bazy zablokowanych tokenów i usunąć dopiero jak wygaśnie

To zablokowane tokeny miałbym trzymać w bazie danych ?

Tylko to się mija z celem. Z JWT się korzysta głównie dlatego że nie potrzeba bazy do niego. Jak i tak masz bazę w której trzymasz tokeny, to równie dobrze mógłbyś w ogóle nie korzystać z JWT, tylko normalne tokeny sesji trzymać w bazie.

0
Ornstein napisał(a):

To zablokowane tokeny miałbym trzymać w bazie danych ?

Jak czas życia tokenu jest krótki to nie ma sensu, bo po prostu masz expired pole i jak czas minie to po prostu nieważny jest token i nic nim nie zrobisz.

Tak dobrze możesz na sesjach sobie zrobić logowanie, jak chcesz mieć ze w momencie kliknięcia logout nie będzie można w żaden sposób zautoryzować się twoimi danymi, które ktoś mógł wyciągnąć z przeglądarki.

2

W prawdziwych projektach a nie jakichś zabawkach to się robi w bazie danych osobną tabelę do przetrzymywania sesji użytkowników. Jak ktoś chce się zalogować to tworzysz w bazie nową sesję, generujesz do niej jakiś token/hash i wrzucasz z poziomu backendu w cookies responsu, dzięki czemu ląduje on później w przeglądarce, Potem w jakimś middlewarze sprawdzasz czy ten cookie istnieje i sesja z nim znajduje się w bazie a jak nie to rzucasz 401, przy wylogowywaniu się usuwasz sesję z bazy danych i tyle, To jest fajne podejscie jak chcesz wyrzucić jakiegoś użytkownika z systemu to robisz mu takie "Revoke from all sessions" i go automatycznie wylogowuje z aplikacji.

0

Zapomnieć po stronie klienta.
Można też mieć listę unieważnionych tokenów po stronie serwera, ale to trochę wypacza sens.

0

@some_ONE: Odpowiem tutaj na twój komentarz, chociaż pewnie nie tobie, bo już to wiesz.
Jak sam pisałeś wyżej token, właściwie tokeny, bo jest ich kilka równolegle, żyje swoim życiem, bo ten ostateczny wygląda coś jak (za jwt.io):

{
  "alg": "HS256",
  "typ": "JWT"
}

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022 //expiration time
}
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  
your-256-bit-secret

)

Czyli trochę danych o formacie i algorytmach użytych do podpisania JWT, payload informujący kto się tym tokenem posługuje i cyfrowy podpis wystacy tokena (AuthServer).

Zaletą takiego rozwiązania jest, że Resource Server nie musi przy każdym żądaniu odpytywać AuthServer, czy aby na pewno ten token jest ok, bo jest w stanie sprawdzić, czy podpis się zgadza i czy jeszcze nie wygasł.

W takim "czystym" układzie jedyne co można zrobić, to zaczekać minutę, godzinę, miesiąc na wygaśnięcie tokenu.
Wylogowanie można obsłużyć na kilka sposobów:

  1. Zapomnieć token po stronie klienta i zaczekać aż wygaśnie, ze świadomością, że jak ktoś już ukradł, to może korzystać.
  2. Uzupełnić payload o dodatkowe dane identyfikujące maszynę (jakieś losowe cookie, IP klienta, typu/wersji klienta).
  3. Utrzymywać "gdzieś" listę unieważnionych tokenów każdorazowo sprawdzać żądanie pod kątem użycia nieważnego tokenu.

Rozwiązanie 3 ma oczywiste wady - konieczne jest trzymanie sesji, albo chociaż informacji o jej unieważnieniu, po stronie klienta. Ponieważ tokenów jest kilka id, refresh, access i mają one różne czasy życia, można te unieważnienia robić na różnych poziomach i zaakceptować ryzyko, że access token o czasie życia 5 minut może wygasać, a refresh token o czasie życia 1 tygodnia musi mieć możliwość unieważnienia i wysterować sobie wydajność vs. bezpieczeństwo w zależności od konkretnego scenariusza.

0

Wiem, wiem.

Zaletą takiego rozwiązania jest, że Resource Server nie musi przy każdym żądaniu odpytywać AuthServer, czy aby na pewno ten token jest ok, bo jest w stanie sprawdzić, czy podpis się zgadza i czy jeszcze nie wygasł.

Tak, a w bieda implementacjach pokazywanych w tutorialach w 99,99% przypadków Resource Server jest jednocześnie Auth Serverem, bo tam jedynym bytem jest aplikacja webowa.
Przy czym dalej istnieją zalety takiego rozwiązania, bo jak już kilka postów wyżej w komentarzu było pisane, taki token w przeciwieństwie do server side'owej sesji nie wymaga weryfikacji z zewnętrznym źródłem danych.
Tylko tak realnie, jakim narzutem w typowej aplikacji jest strzał do jednej tabelki w bazie (to będzie seek po indeksie) albo redisa?

Jak piszemy jakieś ogólnodostępne API (czy to wewnątrz organizacji czy w publiczne) to takie bezstanowe tokeny są zalecane, bo słabo uwierzytelniać zewnętrznych klientów za pomocą identyfikatorów sesyjnych (nie wiadomo czy klientem będzie user/service/jakiś job). Ale jak mamy swoją apkę składającą się z frontendu i backendu (często mylnie utożsamianego z REST API) to szczerze mówiąc ja z doświadczeń wolę pójść w zwykłe ciasteczka sesyjne wbudowane we framework webowy z którego korzystam i fajrant.

Rozwiązanie 3 ma oczywiste wady - konieczne jest trzymanie sesji, albo chociaż informacji o jej unieważnieniu, po stronie klienta.

Tak, ale ma też oczywiste zalety, których nie ma reszta rozwiązań - pozwala natychmiastowo i definitywnie zakończyć aktualnie trwającą sesję (lub wszystkie sesje danego użytkownika).

1

Koledzy już sporo wyjaśnili, to ja dorzucę moje preferowane rozwiązanie.
API Gateway trzyma sesję użytkowników w Redisie i wymienia je na tokeny JWT dla mikroserwisów (internal).

0

https://dev.to/abrichak/jwt-and-go-how-to-integrate-them-with-security-requirements-eh5 Przykład z poradnkiem dla Go, ale ogarniesz w sumie to blizniacze do @Productionserver

0

@some_ONE: Wszystko zależy od scenariusza, obciążenia i jak prawdopodobny/dotkliwy jest włam. Nie ma "typowej aplikacji", bo każdy ją sobie wyobraża inaczej. Inne wymagania ma aplikacja bankowa z niewielką liczbą użytkowników / żądań, inne stronka do wymiany informacji o kotach z milionem użytkowników, w dodatku z wymogiem google/x/github/FB sign in.

@Productionserver To jest wygodne rozwiązanie, ale to co lata sobie po klastrze jako nagłówek jest ~bezpieczne.

2
LukaszCh233 napisał(a):

Hej, chcę dodać do projektu możliwość wylogowania co ma prowadzić do unieważnienia tokenu. Niestety nie mogę znaleźć sensownego poradnika jak to dobrze zrobić. Jaki jest najlepszy sposób żeby to zrobić? Próbowałem tego:

Rozumiem, że to w celu zabawy?

Bo w typowej aplikacji coś takiego jest zupełnie niepotrzebne.

Pracowałem przy dużym systemie, gdzie było JWT i lista unieważnionych tokenów, ale to było robione na poziomie application firewall / load balancera. Przykład: unieważniamy token, jak ktoś zaloguje się i potem robi bota, który skanuje dane aplikacji, ew robi penetration testy :-).
Programiści nie mieli przy tym nic do roboty, bo całość tp konfiguracja w firewall
(poza okazyjnym supportem, bo kogoś "wylogowało" jak miał przypadkiem <script w haśle).

W normalnych przypadkach (zalogowanie, wylogowanie) w ogóle nie było nawet można unieważnić tokena, bo i po co? Klient chciał się wylogować to zapominał tokena i koniec.

1 użytkowników online, w tym zalogowanych: 0, gości: 1