(zrodzony z tego wątku, ponieważ jest to naprawdę kwestia osobna i nie dotyczy NodeJS itp.)
Wdrażam serwer REST API z uwierzytelnianiem i pomyślnie zaimplementowałem obsługę tokenów JWT, dzięki czemu użytkownik może zalogować się przez punkt końcowy / login z nazwą użytkownika / hasłem, po czym token JWT jest generowany z tajnego serwera i zwracany do klient. Token jest następnie przekazywany od klienta do serwera w każdym uwierzytelnionym żądaniu API, na którym klucz tajny serwera jest używany do weryfikacji tokenu.
Jednak staram się zrozumieć najlepsze praktyki dotyczące tego, jak i w jakim stopniu token powinien być walidowany, aby stworzyć prawdziwie bezpieczny system. Co dokładnie powinno być zaangażowane w „walidację” tokena? Czy wystarczy, że podpis można zweryfikować za pomocą klucza serwera, czy też powinienem również porównać token i / lub ładunek tokenu z niektórymi danymi przechowywanymi na serwerze?
System uwierzytelniania oparty na tokenach będzie tak bezpieczny, jak przekazywanie nazwy użytkownika / hasła w każdym żądaniu, pod warunkiem, że uzyskanie tokena jest równie trudne lub trudniejsze niż uzyskanie hasła użytkownika. Jednak w przykładach, które widziałem, jedyne informacje wymagane do utworzenia tokena to nazwa użytkownika i klucz po stronie serwera. Czy nie oznacza to, że zakładając na minutę, że złośliwy użytkownik uzyska wiedzę o tajemnicy serwera, może teraz tworzyć tokeny w imieniu dowolnego użytkownika, tym samym mając dostęp nie tylko do jednego podanego użytkownika, jak miałoby to miejsce w przypadku hasła uzyskane, ale w rzeczywistości do wszystkich kont użytkowników?
To prowadzi mnie do pytań:
1) Czy walidacja tokena JWT powinna być ograniczona do weryfikacji podpisu samego tokena, polegająca na integralności samego klucza serwera, czy też powinna towarzyszyć mu osobny mechanizm walidacji?
W niektórych przypadkach widziałem połączone użycie tokenów i sesji serwera, w których po pomyślnym zalogowaniu się przez punkt końcowy / login została ustanowiona sesja. Żądania API weryfikują token, a także porównują zdekodowane dane znalezione w tokenie z niektórymi danymi przechowywanymi w sesji. Jednak korzystanie z sesji oznacza używanie plików cookie iw pewnym sensie jest to sprzeczne z celem stosowania podejścia opartego na tokenach. Może to również powodować problemy dla niektórych klientów.
Można sobie wyobrazić serwer przechowujący wszystkie aktualnie używane tokeny w memcache lub podobnym, aby zapewnić, że nawet jeśli klucz serwera zostanie naruszony, tak aby atakujący mógł wygenerować „prawidłowe” tokeny, tylko te dokładne tokeny, które zostały wygenerowane przez punkt końcowy / login zostałby zaakceptowany. Czy to rozsądne, czy po prostu zbędne / przesada?
2) Jeśli weryfikacja podpisu JWT jest jedynym sposobem walidacji tokenów, co oznacza, że integralność tajemnicy serwera jest punktem krytycznym, w jaki sposób należy zarządzać tajemnicami serwera? Czytać ze zmiennej środowiskowej i tworzyć (losowo?) Raz na wdrożony stos? Ponownie aktualizowane lub zmieniane okresowo (a jeśli tak, jak obsługiwać istniejące ważne tokeny, które zostały utworzone przed rotacją, ale muszą zostać zweryfikowane po rotacji, być może wystarczy, jeśli serwer zachowuje bieżący i poprzedni sekret w dowolnym momencie) ? Coś innego?
Może jestem po prostu zbyt paranoikiem, jeśli chodzi o ryzyko ujawnienia tajemnicy serwera, co jest oczywiście bardziej ogólnym problemem, który należy rozwiązać we wszystkich sytuacjach kryptograficznych ...
RSAPrivateKey privateKey
??Odpowiedzi:
Bawiłem się również tokenami do mojej aplikacji. Chociaż w żadnym wypadku nie jestem ekspertem, mogę podzielić się niektórymi swoimi doświadczeniami i przemyśleniami w tej sprawie.
Istotą JWT jest zasadniczo integralność. Zapewnia mechanizm umożliwiający serwerowi weryfikację, czy token, który został mu dostarczony, jest oryginalny i został dostarczony przez serwer. Zapewnia to podpis wygenerowany przez Twój sekret. Więc tak, jeśli twój sekret w jakiś sposób wycieknie, ta osoba może wygenerować tokeny, które twój serwer pomyśli, że są jego własnymi. System oparty na tokenach byłby nadal bezpieczniejszy niż system nazwy użytkownika / hasła tylko ze względu na weryfikację podpisu. W tym przypadku, jeśli ktoś i tak ma twój sekret, twój system ma inne problemy z bezpieczeństwem niż ktoś, kto robi fałszywe tokeny (a nawet wtedy, po prostu zmiana sekretu zapewnia, że wszystkie tokeny utworzone ze starym sekretem są teraz nieważne).
Jeśli chodzi o ładunek, podpis powie ci tylko, że dostarczony token był dokładnie taki, jak był w momencie wysłania go przez serwer. sprawdzenie, czy zawartość ładunków jest prawidłowa lub odpowiednia dla Twojej aplikacji, zależy oczywiście od Ciebie.
W przypadku pytań:
1.) Z mojego ograniczonego doświadczenia wynika, że zdecydowanie lepiej zweryfikować swoje tokeny za pomocą drugiego systemu. Samo sprawdzenie podpisu oznacza po prostu, że token został wygenerowany z Twoim sekretem. Przechowywanie dowolnych utworzonych tokenów w jakiejś bazie danych (redis, memcache / sql / mongo lub innym miejscu) to fantastyczny sposób na zapewnienie, że akceptujesz tylko tokeny utworzone przez Twój serwer. W tym scenariuszu, nawet jeśli Twój sekret zostanie ujawniony, nie będzie to miało większego znaczenia, ponieważ żadne wygenerowane tokeny i tak nie będą ważne. Oto podejście, które stosuję w moim systemie - wszystkie wygenerowane tokeny są przechowywane w bazie danych (redis) i przy każdym żądaniu sprawdzam, czy token znajduje się w mojej bazie danych, zanim go zaakceptuję. W ten sposób tokeny mogą zostać unieważnione z dowolnego powodu, na przykład tokeny, które zostały w jakiś sposób uwolnione, wylogowanie użytkownika, zmiany hasła, zmiany tajne itp.
2.) Jest to coś, w czym nie mam dużego doświadczenia i nadal aktywnie badam, ponieważ nie jestem specjalistą od bezpieczeństwa. Jeśli znajdziesz jakieś zasoby, możesz je opublikować tutaj! Obecnie używam tylko klucza prywatnego, który ładuję z dysku, ale oczywiście nie jest to najlepsze ani najbezpieczniejsze rozwiązanie.
źródło
Oto kilka rzeczy, które należy wziąć pod uwagę podczas implementacji tokena JWT w aplikacji:
Utrzymuj stosunkowo krótki czas życia JWT i zarządzaj nim na serwerze. Jeśli tego nie zrobisz, a później będziesz potrzebować więcej informacji w swoich tokenach JWT, będziesz musiał albo obsługiwać dwie wersje, albo poczekać, aż wygaśnie ważność starszych tokenów JWT, zanim będzie można wprowadzić zmianę. Możesz łatwo zarządzać nim na serwerze, jeśli spojrzysz tylko na
iat
pole w jwt i zignorujeszexp
pole.Rozważ umieszczenie adresu URL żądania w swoim tokenie JWT. Na przykład, jeśli chcesz, aby Twój token JWT był używany w punkcie końcowym
/my/test/path
, dołącz pole takie jak'url':'/my/test/path'
w swoim tokenie JWT, aby mieć pewność, że będzie używane tylko w tej ścieżce. Jeśli tego nie zrobisz, może się okazać, że ludzie zaczną używać twoich tokenów JWT na innych punktach końcowych, nawet tych, dla których nie zostali stworzeni. Możesz także rozważyć dołączenie zamiast tego adresu md5 (url), ponieważ duży adres URL w tokenie JWT sprawi, że token JWT będzie o wiele większy i mogą stać się całkiem duże.Wygaśnięcie tokena JWT powinno być konfigurowalne w każdym przypadku użycia, jeśli tokeny JWT są implementowane w interfejsie API. Na przykład, jeśli masz 10 punktów końcowych dla 10 różnych przypadków użycia dla tokena JWT, upewnij się, że możesz sprawić, by każdy punkt końcowy akceptował tokeny JWT, które wygasają w różnym czasie. Pozwala to blokować niektóre punkty końcowe bardziej niż inne, jeśli na przykład dane obsługiwane przez jeden punkt końcowy są bardzo wrażliwe.
Zamiast po prostu wygasać tokeny JWT po pewnym czasie, rozważ wdrożenie tokena JWT obsługującego oba te elementy:
Wszystkie niepowodzenia uwierzytelniania JWT powinny generować nagłówek odpowiedzi „błąd”, który określa przyczynę niepowodzenia uwierzytelniania JWT. np. „wygasł”, „nie ma już zastosowań”, „unieważniono” itp. Pomaga to realizatorom dowiedzieć się, dlaczego ich tokeny JWT zawodzą.
Rozważ zignorowanie „nagłówka” tokenów JWT, ponieważ ujawniają one informacje i zapewniają kontrolę hakerom. Dotyczy to głównie
alg
pola w nagłówku - zignoruj to i po prostu załóż, że nagłówek jest tym, co chcesz obsługiwać, ponieważ pozwala to uniknąć hakerów próbujących użyćNone
algorytmu, który usuwa kontrolę bezpieczeństwa podpisu.Tokeny JWT powinny zawierać identyfikator określający, która aplikacja wygenerowała token. Na przykład, jeśli token JWT jest tworzony przez 2 różnych klientów, mychat i myclassifiedsapp, każdy z nich powinien zawierać nazwę projektu lub coś podobnego w polu „iss” w tokenie JWT, np. „Iss”: „mychat”
iat
(wydane o) zamiastexp
(wygaśnięcie) w tokenach JWT. Czemu? Ponieważiat
zasadniczo oznacza to, kiedy został utworzony token JWT, pozwala to na dostosowanie na serwerze terminu wygaśnięcia tokena JWT na podstawie daty utworzenia. Jeśli ktoś minieexp
20 lat w przyszłości, JWT w zasadzie żyje wiecznie! Zwróć uwagę, że automatycznie tracisz czas na tokeny JWT, jeśliiat
są w przyszłości, ale zostaw trochę miejsca na poruszanie się (np. 10 sekund), na wypadek gdyby czas klienta był nieco niezsynchronizowany z czasem serwera./mysite/userInfo?jwt=XXX
i że ten adres URL jest zapisywany w pamięci podręcznej. Wylogowują się, a kilka minut później zwykły użytkownik loguje się do Twojej aplikacji. Otrzymają zawartość z pamięci podręcznej - z informacjami o superużytkowniku! Zwykle dzieje się to rzadziej na kliencie, a częściej na serwerze, szczególnie w przypadkach, gdy używasz CDN, takiego jak Akamai, i pozwalasz niektórym plikom żyć dłużej. Można to naprawić, dołączając odpowiednie informacje o użytkowniku do adresu URL i sprawdzając je na serwerze, nawet w przypadku żądań zapisanych w pamięci podręcznej, na przykład/mysite/userInfo?id=52&jwt=XXX
źródło
created_by
, jest już roszczeniem do tego w JWT i nazywa sięiss
(emitent).Nie sądzę, żebym był ekspertem, ale chciałbym podzielić się przemyśleniami na temat Jwt.
1: Jak powiedział Akshay, lepiej mieć drugi system do weryfikacji tokena.
a .: Sposób, w jaki sobie z tym radzę: przechowuję wygenerowany hash w pamięci sesji wraz z czasem wygaśnięcia. Aby zweryfikować token, musi zostać wydany przez serwer.
b .: Jest co najmniej jedna rzecz, którą należy sprawdzić w używanej metodzie podpisu. np .:
Niektóre biblioteki walidujące JWT zaakceptowałyby ten bez sprawdzania skrótu. Oznacza to, że nie znając twojej soli użytej do podpisania tokena, haker może przyznać sobie pewne prawa. Zawsze upewnij się, że tak się nie stanie. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
c .: Korzystanie z pliku cookie z identyfikatorem sesji nie byłoby przydatne do weryfikacji tokena. Gdyby ktoś chciał przejąć sesję użytkownika lambda, musiałby po prostu użyć sniffera (np. Wireshark). Ten haker miałby obie informacje w tym samym czasie.
Sposób, w jaki sobie z tym radzę, jest powiązany z punktem 1.a. : Mam sekret zmieszany ze zmienną losową. Sekret jest unikalny dla każdego tokena.
Jeśli chcesz mieć najlepsze możliwe zabezpieczenia, nie powinieneś ślepo przestrzegać najlepszych praktyk. Najlepszym sposobem jest zrozumienie tego, co robisz (myślę, że jest w porządku, gdy widzę twoje pytanie), a następnie ocena potrzebnych zabezpieczeń. A jeśli Mosad chce mieć dostęp do twoich poufnych danych, zawsze znajdzie sposób. (Podoba mi się ten wpis na blogu: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )
źródło
Wiele dobrych odpowiedzi. Zintegruję niektóre odpowiedzi, które moim zdaniem są najbardziej odpowiednie, i dodam więcej sugestii.
1) Czy walidacja tokena JWT powinna być ograniczona do weryfikacji podpisu samego tokena, polegająca na integralności samego klucza serwera, czy też powinna towarzyszyć mu osobny mechanizm walidacji?
Nie, z powodów niezwiązanych z ujawnieniem tajemnicy symbolicznej. Za każdym razem, gdy użytkownik loguje się za pomocą nazwy użytkownika i hasła, serwer autoryzacji powinien przechowywać albo wygenerowany token, albo metadane dotyczące wygenerowanego tokenu. Potraktuj te metadane jako rekord autoryzacji. Dana para użytkownika i aplikacji powinna mieć tylko jeden ważny token lub autoryzację w danym momencie. Przydatne metadane to identyfikator użytkownika powiązany z tokenem dostępu, identyfikator aplikacji i czas wydania tokenu dostępu (co pozwala na odwołanie istniejących tokenów dostępu i wydanie nowego tokenu dostępu). Przy każdym żądaniu API sprawdź, czy token zawiera prawidłowe metadane. Musisz utrwalać informacje o tym, kiedy wydano poszczególne tokeny dostępu, aby użytkownik mógł unieważnić istniejące tokeny dostępu, jeśli dane uwierzytelniające jego konta zostały naruszone, a następnie zalogować się ponownie i zacząć używać nowego tokenu dostępu. Spowoduje to zaktualizowanie bazy danych o czas wystawienia tokena dostępu (utworzony czas autoryzacji). Na każdym żądaniu API sprawdź, czy czas wydania tokena dostępu jest późniejszy niż utworzony czas autoryzacji.
Inne środki bezpieczeństwa obejmowały niezalogowanie tokenów JWT i wymaganie bezpiecznego algorytmu podpisywania, takiego jak SHA256.
2) Jeśli weryfikacja podpisu JWT jest jedynym sposobem walidacji tokenów, co oznacza, że integralność tajemnicy serwera jest punktem krytycznym, w jaki sposób należy zarządzać tajemnicami serwera?
Naruszenie tajemnic serwera pozwoliłoby atakującemu na wydanie tokenów dostępu dla dowolnego użytkownika, a przechowywanie danych tokena dostępu w kroku 1 niekoniecznie uniemożliwiłoby serwerowi zaakceptowanie tych tokenów dostępu. Załóżmy na przykład, że użytkownik otrzymał token dostępu, a następnie osoba atakująca generuje token dostępu dla tego użytkownika. Czas autoryzacji tokena dostępu byłby ważny.
Jak mówi Akshay Dhalwala, jeśli Twój klucz tajny po stronie serwera zostanie naruszony, masz większe problemy, z którymi musisz sobie poradzić, ponieważ oznacza to, że atakujący włamał się do Twojej sieci wewnętrznej, repozytorium kodu źródłowego lub obu.
Jednak system mający na celu złagodzenie zniszczenia poufnego sekretu serwera i uniknięcie przechowywania sekretów w kodzie źródłowym obejmuje rotację tajnych tokenów za pomocą usługi koordynacji, takiej jak https://zookeeper.apache.org. Użyj zadania cron, aby wygenerować sekret aplikacji co kilka godzin (bez względu na to, jak długo twoje tokeny dostępu są ważne) i prześlij zaktualizowany sekret do Zookeeper. Na każdym serwerze aplikacji, który musi znać sekret tokena, skonfiguruj klienta ZK, który jest aktualizowany po każdej zmianie wartości węzła ZK. Przechowuj główny i drugorzędny sekret, a za każdym razem, gdy zostanie zmieniony klucz, ustaw nowy klucz tajny na główny, a stary na drugorzędny. W ten sposób istniejące prawidłowe tokeny będą nadal ważne, ponieważ zostaną sprawdzone pod kątem dodatkowego klucza tajnego. Do czasu zastąpienia dodatkowego klucza tajnego starym kluczem głównym wszystkie tokeny dostępu wydane z tym sekretem drugorzędnym i tak stracą ważność.
źródło
IETF prowadzi RFC w grupie roboczej oAuth, patrz: https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html
źródło