Unieważnianie tokenów internetowych JSON
Dla nowego węzła.projekt js, nad którym pracuję, myślę o przejściu z podejścia sesyjnego opartego na plikach cookie (przez to mam na myśli przechowywanie identyfikatora do sklepu z wartością klucza zawierającego sesje użytkownika w przeglądarce użytkownika) do podejścia opartego na tokenie (bez przechowywania wartości klucza) przy użyciu tokenów internetowych JSON (jwt).
Projekt jest grą, która wykorzystuje socket.io - posiadanie sesji tokenowej byłoby przydatne w takim scenariuszu, w którym będzie wiele kanałów komunikacji w jednym sesja (WWW i socket.io)
Jak można zapewnić unieważnienie tokena / sesji z serwera przy użyciu podejścia jwt?
Chciałem również zrozumieć, na jakie częste (lub rzadkie) pułapki/ataki powinienem zwrócić uwagę przy tego rodzaju paradygmacie. Na przykład, jeśli ten paradygmat jest podatny na te same/różne rodzaje ataków, co podejście oparte na przechowywaniu sesji / plikach cookie.
Więc powiedzmy, że mam następujące (zaadaptowane z to i to):
Session Store Login:
app.get('/login', function(request, response) {
var user = {username: request.body.username, password: request.body.password };
// Validate somehow
validate(user, function(isValid, profile) {
// Create session token
var token= createSessionToken();
// Add to a key-value database
KeyValueStore.add({token: {userid: profile.id, expiresInMinutes: 60}});
// The client should save this session token in a cookie
response.json({sessionToken: token});
});
}
Login Oparty Na Tokenie:
var jwt = require('jsonwebtoken');
app.get('/login', function(request, response) {
var user = {username: request.body.username, password: request.body.password };
// Validate somehow
validate(user, function(isValid, profile) {
var token = jwt.sign(profile, 'My Super Secret', {expiresInMinutes: 60});
response.json({token: token});
});
}
--
Wylogowanie (lub unieważnienie) w podejściu do przechowywania sesji wymagałoby aktualizacji do KeyValueStore baza danych z podanym tokenem.
Wydaje się, że taki mechanizm nie istniałby w podejściu opartym na tokenie, ponieważ sam token zawierałby informacje, które normalnie istniałyby w magazynie klucz-wartość.
16 answers
Ja też badałem to pytanie i chociaż żaden z poniższych pomysłów nie jest kompletnym rozwiązaniem, mogą pomóc innym wykluczyć pomysły lub dostarczyć dalsze.
1) po prostu usuń token z klienta
Oczywiście nie robi to nic dla bezpieczeństwa po stronie serwera, ale zatrzymuje atakującego, usuwając token z istnienia (np. musieli ukraść token przed wylogowaniem).
2) Utwórz czarną listę tokenów
Ty może przechowywać nieprawidłowe tokeny do ich początkowej daty wygaśnięcia i porównywać je z przychodzącymi żądaniami. Wydaje się to negować powód, dla którego Przejście W pełni token opiera się na pierwszym miejscu, ponieważ trzeba by dotknąć bazy danych dla każdego żądania. Rozmiar pamięci byłby jednak prawdopodobnie niższy, ponieważ trzeba byłoby przechowywać tylko tokeny, które znajdowały się między wylogowaniem i czasem wygaśnięcia (jest to przeczucie i zdecydowanie zależy od kontekstu).
3) po prostu skróć czas wygaśnięcia tokenu i często je obracać
Jeśli utrzymasz czas wygaśnięcia tokenu w odpowiednio krótkich odstępach czasu, a uruchomiony klient będzie śledzić i żądać aktualizacji, gdy zajdzie taka potrzeba, numer 1 będzie skutecznie działał jako kompletny system wylogowania. Problem z tą metodą polega na tym, że uniemożliwia utrzymanie zalogowanego użytkownika pomiędzy zamknięciami kodu klienta(w zależności od tego, jak długi jest okres ważności).
Plany Awaryjne
Gdyby kiedykolwiek zaistniała sytuacja awaryjna, lub token użytkownika został naruszony, jedną z rzeczy, które możesz zrobić, to pozwolić użytkownikowi na zmianę podstawowego identyfikatora użytkownika z jego poświadczeniami logowania. Spowoduje to unieważnienie wszystkich powiązanych Tokenów, ponieważ powiązanego użytkownika nie można już znaleźć.
Chciałem również zauważyć, że dobrym pomysłem jest włączenie daty ostatniego logowania do tokena, aby móc wymusić relogin po pewnym odległym okresie czasu.
Pod względem podobieństw/różnic w odniesieniu do ataki za pomocą tokenów, ten post odpowiada na pytanie: http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-16 08:57:24
Pomysły zamieszczone powyżej są dobre, ale bardzo prostym i łatwym sposobem unieważnienia wszystkich istniejących JWTs jest po prostu zmiana tajemnicy.
Jeśli twój serwer tworzy JWT, podpisuje go sekretem (JWS), a następnie wysyła go do klienta, po prostu zmiana tajemnicy unieważni wszystkie istniejące tokeny i wymagać wszystkich użytkowników, aby uzyskać nowy token do uwierzytelnienia, ponieważ ich stary token nagle staje się nieważny zgodnie z serwerem.
Nie wymaga żadnych modyfikacji w rzeczywistym zawartość tokena (lub ID wyszukiwania).
Oczywiście działa to tylko w nagłych przypadkach, gdy chcesz, aby wszystkie istniejące tokeny wygasły, ponieważ na wygaśnięcie tokenu wymagane jest jedno z powyższych rozwiązań (takich jak krótki czas wygaśnięcia tokenu lub unieważnienie przechowywanego klucza wewnątrz tokenu).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 06:36:37
To przede wszystkim długi komentarz wspierający i bazujący na odpowiedzi @mattway
Podane:
Niektóre z innych proponowanych rozwiązań na tej stronie opowiadają się za trafianiem do magazynu danych na każde żądanie. Jeśli trafisz w główny magazyn danych, aby zweryfikować każde żądanie uwierzytelnienia, widzę mniej powodów, aby używać JWT zamiast innych ustalonych mechanizmów uwierzytelniania tokenów. Zasadniczo uczyniłeś JWT stateful, zamiast stateless, Jeśli za każdym razem przechodzisz do magazynu danych.
(jeśli Twoja witryna otrzymuje dużą ilość nieautoryzowanych żądań, a JWT odmówi ich bez uderzania w magazyn danych, co jest pomocne. Są prawdopodobnie inne przypadki użycia tego typu.)
Podane:
Prawdziwie bezpaństwowe uwierzytelnianie JWT nie może być osiągnięte dla typowej, rzeczywistej aplikacji internetowej, ponieważ bezpaństwowe JWT nie ma sposobu, aby zapewnić natychmiastowe i bezpieczne wsparcie dla następujących ważnych przypadków użycia:
Konto użytkownika jest usunięte / zablokowane / zawieszone.
Hasło użytkownika zostało zmienione.
Role lub uprawnienia użytkownika są zmieniane.
Użytkownik jest wylogowany przez admina.
Wszelkie inne krytyczne dane aplikacji w tokenie JWT są zmieniane przez Administratora Witryny.
W takich przypadkach nie można czekać na wygaśnięcie tokena. Unieważnienie tokenu musi nastąpić natychmiast. Ponadto nie można ufać klientowi, że nie zachowa i nie użyje kopii starego tokena, czy to z zamiarem złośliwym, czy nie.
Dlatego: Myślę, że odpowiedź z @ matt-way, # 2 TokenBlackList, byłaby najskuteczniejszym sposobem na dodanie wymaganego stanu do uwierzytelniania opartego na JWT.
Masz czarną listę, która przechowuje te żetony do czasu ich wygaśnięcia. Lista tokenów będzie dość mała w porównaniu do całkowitej liczby użytkowników, ponieważ musi tylko utrzymywać tokeny na czarnej liście aż do ich wygaśnięcia. Zaimplementowałbym umieszczając unieważnione tokeny w redis, memcached lub innym in-memory magazyn danych obsługujący Ustawianie czasu wygaśnięcia klucza.
Nadal musisz wykonać połączenie do bazy danych w pamięci dla każdego żądania uwierzytelniania, które przekazuje początkowy JWT auth, ale nie musisz tam przechowywać kluczy dla całego zestawu użytkowników. (Co może, ale nie musi być wielką sprawą dla danej strony.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-24 14:29:26
Zapisałbym numer wersji jwt na modelu użytkownika. Nowe tokeny jwt ustawiłyby swoją wersję na to.
Kiedy sprawdzasz poprawność jwt, po prostu sprawdź, czy ma on Numer wersji równy bieżącej wersji JWT użytkowników.
Za każdym razem, gdy chcesz unieważnić stare jwts, po prostu podbijaj numer wersji JWT użytkowników.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-15 23:46:18
Jeszcze tego nie próbowałem, a jest to Złożoność polega na uniknięciu wywołania magazynu danych po stronie serwera na żądanie informacji o użytkowniku. Większość innych rozwiązań wymaga wyszukiwania db na żądanie do magazynu sesji użytkownika. To jest w porządku w niektórych scenariuszach, ale to zostało stworzone w celu uniknięcia takich połączeń i zrobić cokolwiek wymagane stan po stronie serwera jest bardzo mały. w końcu odtworzysz sesję po stronie serwera, jednak mały, aby zapewnić wszystkie funkcje unieważniania siły. Ale jeśli chcesz to zrobić tutaj jest sedno:
Cele:
- ograniczenie korzystania z magazynu danych (bez stanu).
- możliwość wymusić wylogowanie wszystkich użytkowników.
- możliwość wymusić wylogowanie każdej osoby w dowolnym momencie.
- możliwość żądania ponownego wprowadzenia hasła po pewnym czasie.
- Umiejętność pracy z wieloma klientami.
- możliwość wymuszenia ponownego logowania, gdy użytkownik kliknie Wyloguj od konkretnego klienta. (Aby uniemożliwić komuś "usunięcie" tokena klienta po odejściu użytkownika-Zobacz komentarze, aby uzyskać dodatkowe informacje)
Rozwiązanie:
- użyj krótkotrwałych (refresh-token .
- każde żądanie sprawdza datę ważności tokena auth lub refresh.
- gdy token dostępu wygaśnie, klient używa tokenu odświeżania do odśwież token dostępu.
- podczas sprawdzania tokenu odświeżania serwer sprawdza małą czarną listę identyfikatorów użytkowników - jeśli zostanie znaleziony Odrzuć żądanie odświeżenia.
- Gdy klient nie ma poprawnego (nie wygasłego) tokena odświeżania lub auth, użytkownik musi się zalogować ponownie, ponieważ wszystkie inne żądania zostaną odrzucone.
- na żądanie logowania, sprawdź magazyn danych użytkownika pod kątem bana.
- przy wylogowaniu-dodaj tego użytkownika do czarnej listy sesji, aby musiał się ponownie zalogować. Będziesz musiał przechowywać dodatkowe informacje, aby nie wylogować ich ze wszystkich urządzeń w środowisku wielu urządzeń, ale można to zrobić, dodając pole urządzenia do czarnej listy użytkownika.
- aby wymusić ponowne wejście po X czasie-zachowaj datę ostatniego logowania w tokenie auth i sprawdź ją na żądanie.
- aby wymusić wylogowanie wszystkich użytkowników-zresetuj klucz hash tokena.
Wymaga to utrzymywania czarnej listy (stanu) na serwerze, zakładając, że tabela użytkowników zawiera informacje o zbanowanych użytkownikach. The invalid sessions blacklist-to lista identyfikatorów użytkowników. Ta czarna lista jest sprawdzana tylko podczas żądania odświeżenia tokena. Wpisy są wymagane, aby żyć na nim tak długo, jak odśwież token TTL. Po wygaśnięciu tokenu odświeżania użytkownik musi się zalogować ponownie.
Wady:
- nadal wymagane do przeprowadzenia wyszukiwania magazynu danych w żądaniu odśwież tokena.
- nieprawidłowe tokeny mogą nadal działać dla TTL tokena dostępu.
Plusy:
- zapewnia pożądana funkcjonalność.
- Akcja odśwież token jest ukryta przed użytkownikiem przy normalnym działaniu.
- wymagane tylko do wyszukiwania magazynu danych przy żądaniach odświeżania zamiast każdego żądania. czyli 1 co 15 min zamiast 1 na sekundę.
- minimalizuje stan po stronie serwera do bardzo małej czarnej listy.
Z tym rozwiązaniem zapis danych w pamięci, takich jak reddis, nie jest potrzebny, przynajmniej nie dla informacji o użytkowniku, jak ty, ponieważ serwer wykonuje tylko wywołanie db co 15 lub tak minut. Jeśli używasz reddis, przechowywanie listy poprawnych / nieprawidłowych sesji byłoby bardzo szybkim i prostszym rozwiązaniem. Nie ma potrzeby odświeżania tokena. Każdy token auth będzie miał identyfikator sesji i identyfikator urządzenia, mogą być przechowywane w tabeli reddis podczas tworzenia i unieważniane w razie potrzeby. Następnie będą sprawdzane przy każdym żądaniu i odrzucane, gdy będą nieważne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-25 18:31:44
Jeśli chodzi o to, czy jest to możliwe, to nie jest to możliwe. Następnie, gdy użytkownik wyloguje się, zapisz ten znacznik czasu w rekordzie użytkownika. Podczas walidacji JWT po prostu porównaj iat
z ostatnio wylogowanym znacznikiem czasu. Jeśli iat
jest starszy, to nie jest poprawny. Tak, musisz iść do DB, ale i tak zawsze będę wyciągał Rekord użytkownika, jeśli JWT jest inaczej ważny.
Główną wadą, jaką widzę, jest to, że wylogowałby ich ze wszystkich sesji, jeśli są w wielu przeglądarkach lub mają klienta mobilnego.
Może to być również dobry mechanizm unieważniania wszystkich JWT w systemie. Część kontroli może być porównywana z globalnym znacznikiem czasu ostatniego ważnego iat
czasu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-25 06:37:37
Trochę się spóźniłem, ale myślę, że mam przyzwoite rozwiązanie.
Mam kolumnę "last_password_change" w mojej bazie danych, która przechowuje datę i godzinę ostatniej zmiany hasła. Zapisuję również datę / godzinę wydania w JWT. Podczas sprawdzania tokenu sprawdzam, czy hasło zostało zmienione po wystawieniu tokena i czy było token jest odrzucany, mimo że jeszcze nie wygasł.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-25 21:13:53
Możesz mieć pole "last_key_used" w DB w dokumencie/rekordzie użytkownika.
Gdy użytkownik loguje się za pomocą user I pass, generuje nowy losowy ciąg znaków, przechowuje go w polu last_key_used i dodaje do ładunku podczas podpisywania tokenu.
Gdy użytkownik loguje się za pomocą tokenu, sprawdź last_key_used w DB, aby pasował do tego w tokenie.
Następnie, gdy użytkownik wykona na przykład wylogowanie lub jeśli chcesz unieważnić token, po prostu zmień to pole" last_key_used " do innej losowej wartości i kolejne kontrole nie powiodą się, zmuszając użytkownika do zalogowania się z userem i ponownego podania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-12 01:32:45
- Podaj 1 dzień ważności tokenów
- utrzymuj codzienną czarną listę.
- Umieść unieważnione / wylogowane tokeny na czarnej liście
Aby sprawdzić poprawność tokenu, najpierw sprawdź czas wygaśnięcia tokenu, a następnie czarną listę, jeśli token nie wygasł.
W przypadku długich sesji powinien istnieć mechanizm wydłużania czasu wygaśnięcia tokena.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-29 08:11:41
Spóźnienie na imprezę, moje dwa grosze są podane poniżej po kilku badaniach. Podczas wylogowywania się upewnij się, że następujące rzeczy się dzieją...
Wyczyść magazyn/sesję klienta
Zaktualizuj tabelę użytkownika Data ostatniego logowania i data wylogowania za każdym razem, gdy nastąpi logowanie lub wylogowanie. Tak więc data logowania zawsze powinna być większa niż data wylogowania (lub zachowaj datę wylogowania null, Jeśli bieżący status to login i jeszcze się nie wylogował)
Jest to o wiele prostsze niż utrzymywanie dodatkowych tabela czarnej listy i regularnego oczyszczania. Obsługa wielu urządzeń wymaga dodatkowej tabeli do przechowywania loggedIn, dat wylogowania z dodatkowymi szczegółami, takimi jak system operacyjny lub dane Klienta.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-04 22:47:17
Dlaczego po prostu nie użyć twierdzenia jti (nonce) i zapisać to na liście jako pole rekordu użytkownika (zależy od db, ale przynajmniej lista oddzielona przecinkami jest w porządku)? Nie ma potrzeby oddzielnego wyszukiwania, jak zauważyli inni, prawdopodobnie chcesz uzyskać Rekord użytkownika i tak, a w ten sposób możesz mieć wiele ważnych tokenów dla różnych instancji klienta ("Wyloguj się wszędzie" może zresetować listę do pustej)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-09 15:01:59
Zrobiłem to w następujący sposób:
- Wygeneruj
unique hash
, a następnie przechowaj go w redis i Twoim JWT . Można to nazwać sesją- będziemy również przechowywać liczbę żądań, które wykonał dany JWT - za każdym razem, gdy JWT jest wysyłany na serwer, zwiększamy liczbę całkowitą żądań. (jest to opcjonalne)
Więc kiedy użytkownik loguje się, tworzony jest unikalny hash, przechowywany w redis i wstrzykiwany do twojego JWT .
Gdy użytkownik spróbuje odwiedzić chroniony punkt końcowy, pobierze unikalny hash sesji z JWT , zapyta redis i sprawdzi, czy pasuje!
Możemy rozszerzyć z tego i uczynić nasz JWT jeszcze bardziej bezpiecznym, oto jak:
Każdy xżąda konkretnego JWT, generujemy nową unikalną sesję, przechowujemy ją w naszym JWT, a następnie Czarna lista poprzedniej.
Oznacza to, że JWT jest ciągle się zmienia i przestaje być nieświeży JWT jest hakowany, kradziony lub coś innego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-26 08:49:18
Zachowaj taką listę w pamięci
user_id revoke_tokens_issued_before
-------------------------------------
123 2018-07-02T15:55:33
567 2018-07-01T12:34:21
Jeśli Twoje tokeny wygasną w ciągu tygodnia, wyczyść lub zignoruj starsze rekordy. Zachowaj również tylko najnowszy zapis każdego użytkownika. Rozmiar listy będzie zależał od tego, jak długo przechowujesz tokeny i jak często użytkownicy wycofują swoje tokeny. Używaj db tylko wtedy, gdy tabela się zmieni. Załaduj tabelę do pamięci po uruchomieniu aplikacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-02 19:01:58
Unique per user string i Global string hashed together
, aby służyć jako tajna część JWT, umożliwiają zarówno indywidualne, jak i globalne unieważnienie tokenu. Maksymalna elastyczność kosztem wyszukiwania/odczytu db podczas żądania auth. Również łatwe do buforowania, ponieważ rzadko się zmieniają.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-30 15:10:03
Co jeśli po prostu wygenerujesz nowy token z serwera z expiresIn: 0 i oddać klientowi i zapisać w ciastku ?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-02 11:43:59
Po prostu zapisuję token do tabeli users, po zalogowaniu zaktualizuję nowy token, a gdy auth będzie równy aktualnemu jwt użytkownika.
Myślę, że to nie jest najlepsze rozwiązanie, ale to działa dla mnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-22 15:10:00