JWT (JSON Web Token)
| Po angielsku | JSON Web Token (JWT) |
|---|---|
| Którą formą pisać | bez tłumaczenia Skrót, nazwa własna albo nazwa standardu — nie tłumaczymy w ogóle. |
| Inne formy | token JWT · podpisany token JSON (opisowo) |
| Kategoria | Kryptografia, tożsamość i uwierzytelnianie |
JWT (JSON Web Token) to zwarty, bezpieczny do umieszczenia w adresie URL format przekazywania twierdzeń (claims) między dwiema stronami [1]. Twierdzenia zapisuje się jako obiekt JSON, który jest podpisany albo zaszyfrowany [1]. Serwis, który zna klucz, może sprawdzić token bez odpytywania bazy danych. Standard opisuje RFC 7519 z maja 2015 roku; autorzy sugerują wymowę jak angielskie słowo „jot” [1].
Jak działa
Najczęściej spotykany JWT to token podpisany. Składa się z trzech części rozdzielonych kropkami: nagłówka, zestawu twierdzeń i podpisu, każda zakodowana w base64url [1] [3]. Nagłówek podaje algorytm, na przykład HS256 (HMAC ze wspólnym sekretem) albo RS256 czy ES256 (podpis kryptografią asymetryczną). Po zdekodowaniu dwie pierwsze części wyglądają tak:
{ "alg": "HS256", "typ": "JWT" }
{ "iss": "https://example.com", "sub": "42", "aud": "api.example.com",
"iat": 1790000000, "exp": 1790003600 }
Kodowanie base64url nie jest szyfrowaniem. OWASP przypomina, że treść tokenu może odczytać każdy, kto go przechwyci [3]. Poufność daje dopiero wariant szyfrowany (JWE) [1].
RFC 7519 rejestruje siedem standardowych twierdzeń [1].
| Twierdzenie | Znaczenie |
|---|---|
iss | kto wystawił token [1] |
sub | kogo token dotyczy [1] |
aud | dla kogo token jest przeznaczony [1] |
exp, nbf | od kiedy i do kiedy wolno go przyjąć [1] |
iat, jti | kiedy wystawiony i jego unikalny identyfikator [1] |
Typowe użycie w API pokazuje schemat poniżej.
JWT to także format ID Tokena w OpenID Connect, czyli potwierdzenia logowania w SSO [4]. W tym zastosowaniu aplikacja musi sprawdzić podpis, czas ważności i to, czy claim aud zawiera jej identyfikator klienta [4].
W WordPressie
WordPress nie używa JWT w rdzeniu. Podręcznik REST API wymienia jako wbudowane uwierzytelnianie ciasteczkami z nonce oraz hasła aplikacji od wersji 5.6, a obsługę JSON Web Tokens odsyła do wtyczek [5].
Popularna wtyczka JWT Authentication for WP REST API wydaje token pod adresem /wp-json/jwt-auth/v1/token po podaniu loginu i hasła [6]. Sekret podpisu definiuje się w wp-config.php stałą JWT_AUTH_SECRET_KEY, domyślnym algorytmem jest HS256, a token jest ważny siedem dni [6]. Z tego wynikają praktyczne wnioski:
- sekret musi być długi i losowy, jak klucze i sole WordPressa, i nie może trafić do repozytorium; zob. zarządzanie sekretami;
- wyciek pliku konfiguracyjnego oznacza możliwość wystawiania tokenów na dowolnego użytkownika, więc ważne jest utwardzanie pliku wp-config.php;
- tydzień ważności to długie okno dla skradzionego tokenu; warto je skrócić.
Zagrożenia i skutki
RFC 8725 zbiera znane ataki na JWT [2]. Najgroźniejsze dotyczą weryfikacji podpisu, czyli słabości opisanej jako CWE-347: produkt nie sprawdza podpisu albo sprawdza go błędnie [8].
- Algorytm none. RFC 7519 przewiduje token niezabezpieczony, z algorytmem
nonei pustym podpisem [1]. Biblioteka, która ufa nagłówkowi, przyjmie taki token jako ważny [2] [3]. - Pomylenie algorytmów. Zmiana RS256 na HS256 w nagłówku może skłonić serwer do użycia publicznego klucza jako sekretu HMAC [2] [3].
- Słaby sekret. Sekret HMAC o małej entropii można odgadnąć atakiem siłowym na przechwyconym tokenie [2].
- Podstawienie tokenu. Token wystawiony dla jednej usługi zostaje przyjęty przez inną, jeśli ta nie sprawdza odbiorcy [2].
- Brak unieważniania. JWT nie ma wbudowanego mechanizmu odwołania, więc skradziony token działa do chwili wygaśnięcia [3].
- Obcy klucz w nagłówku. Token może wskazywać klucz weryfikacji parametrami
jwk,jkualbox5u. Jeśli serwis ufa takiemu kluczowi bez sprawdzenia, czy prowadzi on do zaufanego źródła, atakujący podpisze token własnym kluczem [3].
Brak unieważniania ma konsekwencje architektoniczne. Wylogowanie użytkownika albo zablokowanie konta nie odbiera ważności wydanym już tokenom. Listy odwołanych tokenów rozwiązują problem, ale według OWASP wprowadzają stan po stronie serwera i podważają główną zaletę bezstanowych sesji [3]. W klasycznej aplikacji z sesją w bazie wystarczy usunąć rekord sesji.
Skutkiem jest obejście uwierzytelniania albo przejęcie sesji. OWASP zaznacza, że token w localStorage albo sessionStorage jest dostępny dla skryptu wstrzykniętego przez XSS (Cross-Site Scripting) [3].
Jak się bronić
RFC 8725 zaleca, by biblioteka sprawdzała, czy algorytm z nagłówka jest dokładnie tym, którego serwis oczekuje, oraz by walidować wszystkie operacje kryptograficzne [2]. Klucze muszą mieć wystarczającą entropię [2]; OWASP podaje dla sekretów HMAC minimum 256 bitów [3].
- Ustalać listę dozwolonych algorytmów po stronie serwera i nigdy nie przyjmować
none[3]. - Sprawdzać
exp,nbf,issiaudprzy każdym żądaniu [3]. - Oznaczać typ tokenu nagłówkiem
typi stosować rozłączne reguły dla różnych rodzajów tokenów [2]. - Nie ufać treści twierdzeń; dane z tokenu przed użyciem w zapytaniu trzeba oczyścić jak każde wejście [2].
- Wydawać tokeny krótko żyjące i tam, gdzie to możliwe, wiązać je z nadawcą, na przykład mechanizmem DPoP [3].
- Gdy token czytają różni odbiorcy, wybierać podpis kluczem publicznym (ES256, EdDSA, PS256) zamiast wspólnego sekretu HMAC [3], i planować rotację kluczy.
Historia i przykłady
Przykładem błędnej implementacji jest InfluxDB. W styczniu 2021 roku Sekurak opisał podatność, w której baza przyjmowała token JWT podpisany pustym wspólnym sekretem [7]. Pozwalało to uwierzytelnić się jako dowolny użytkownik [7]. Błąd naprawiono w InfluxDB w 2019 roku, ale poprawka w Debianie (DSA-4823) ukazała się dopiero w 2021 roku [7].
W 2020 roku IETF opublikowało RFC 8725 jako dokument najlepszych praktyk BCP 225 [2]. Zebrało w nim ataki znane z wcześniejszych implementacji, w tym manipulację algorytmem, słabe klucze symetryczne i pomylenie tokenów różnego przeznaczenia [2].
Przypisy
8- [1] RFC 7519: JSON Web Token (JWT) · IETF
- [2] RFC 8725: JSON Web Token Best Current Practices · IETF
- [3] JSON Web Token Cheat Sheet · OWASP
- [4] OpenID Connect Core 1.0 incorporating errata set 2 · OpenID Foundation
- [5] Authentication — REST API Handbook · WordPress.org
- [6] JWT Authentication for WP REST API · WordPress.org
- [7] Ciekawa podatność w implementacji JWT [InfluxDB] · Sekurak
- [8] CWE-347: Improper Verification of Cryptographic Signature · MITRE