Co JSON Web Token vlastně obsahuje

Pohled dovnitř JWT, vysvětlení hlavičky, payloadu a podpisu, standardní claimy a proč dekódování tokenu z Base64url není totéž co jeho ověření.

JSON Web Token (JWT) jsou tři kusy textu spojené tečkami. Každý kus je zakódovaný v Base64url a první dva jsou prostě JSON. Kdokoli, kdo token má, si může přečíst, co je uvnitř. V běžném JWT není zašifrované nic.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9      <- header
eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJBbGV4In0   <- payload
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFW     <- signature

Spojené tečkami to je token, který vkládáte do požadavku. Dekódujte první dvě části a dostanete obyčejný JSON:

{ "alg": "HS256", "typ": "JWT" }
{ "sub": "12345", "name": "Alex" }

Skutečný payload obvykle nese také časová razítka a příjemce:

{
  "iss": "https://auth.example.com",
  "sub": "12345",
  "aud": "api.example.com",
  "iat": 1756000000,
  "exp": 1756003600,
  "roles": ["editor"]
}

Hlavička

Hlavička popisuje, jak je token podepsaný. Nejvíc záleží na dvou polích:

  • alg pojmenovává podpisový algoritmus, například HS256 (HMAC se SHA-256, používá sdílené tajemství) nebo RS256 (podpis RSA se SHA-256, podepisuje se soukromým klíčem a ověřuje veřejným).
  • kid, tedy ID klíče, je nepovinné a příjemci říká, který z několika klíčů byl použit, takže se klíče dají rotovat, aniž by se rozbily existující tokeny.

typ bývá jen JWT a nenese žádný bezpečnostní význam.

Payload a jeho claimy

Payload je objekt JSON tvořený claimy, což je prostě výraz, jakým specifikace označuje tvrzení o subjektu tokenu. Některá jména claimů registruje standard a všechna jsou krátká:

ClaimNázevCo obsahuje
issVydavatelKdo token vytvořil, obvykle URL nebo název služby
subSubjektKoho se token týká, obvykle stabilní ID uživatele
audPříjemceKomu je token určen, aby služba mohla odmítnout tokeny určené jiné službě
expČas vypršeníOkamžik, po kterém se token musí odmítnout
nbfNe dříve nežOkamžik, před kterým se token musí odmítnout
iatVydáno vKdy token vznikl
jtiID tokenuJedinečný identifikátor, užitečný pro sledování nebo blokování jednotlivých tokenů

exp, nbf i iat jsou hodnoty typu NumericDate, tedy sekundy od 1. ledna 1970 UTC. Jsou to sekundy, ne milisekundy. Pokud se časové razítko dekóduje na datum v roce 57 000, čtete milisekundy a musíte je vydělit tisícem.

Všechno ostatní v payloadu je to, co tam vydavatel dal: role, oprávnění, e-mailová adresa, ID nájemce. Protože se soukromé jméno claimu může jednou srazit s registrovaným, dávají vydavatelé ta svá často do jmenného prostoru s prefixem podobným URL.

Podpis

Podpis se počítá nad přesným textem prvních dvou částí spojených tečkou, pomocí algoritmu z hlavičky a klíče. Změňte jediný znak hlavičky nebo payloadu a podpis už nesedí.

U HS256 totéž tajemství podepisuje i ověřuje, takže kdo dokáže token zkontrolovat, dokáže ho také vyrobit. U RS256 nebo ES256 podepisuje soukromý klíč a ověřuje veřejný, což je to, co poskytovateli identity umožňuje vydávat tokeny, které dokáže ověřit mnoho samostatných služeb, aniž by držely jakékoli tajemství.

Base64url není šifrování

Base64url je táž myšlenka jako Base64 se dvěma rozdíly: používá - a _ místo + a /, aby byl výsledek bezpečný v URL, a koncové doplnění znakem = se obvykle odstraňuje. Je to kódování, tedy vratný způsob zápisu bajtů jako textu. Neposkytuje vůbec žádnou důvěrnost.

Do payloadu JWT proto nikdy nedávejte nic citlivého: žádná hesla, žádná čísla karet, žádné interní poznámky o uživateli. Předpokládejte, že člověk, který token drží, i každý, kdo si ho přečte z logu nebo z úložiště prohlížeče, vidí každé pole.

Existuje samostatný formát JWE, který payload šifruje, a má pět částí místo tří. Pokud má váš token tři části, je podepsaný a čitelný.

Dekódování není ověření

Tohle je ten podstatný bod. Dekodér vám ukáže, co token říká. Neřekne vám, jestli je token pravý. Ověření je samostatný krok a potřebuje klíč.

Aby server tokenu skutečně věřil, musí:

  1. Ověřit podpis proti správnému klíči.
  2. Trvat na algoritmu, který očekává, místo aby věřil poli alg v tokenu. Z vynechání tohoto kroku plynou dva klasické útoky: nastavit alg na none a podpis odstranit, a vzít veřejný klíč RSA dané služby a použít ho jako tajemství pro HMAC, takže je ověřovatel RS256 podveden a spustí HS256.
  3. Zkontrolovat exp a, pokud je přítomné, nbf, s tolerancí na malý rozdíl hodin.
  4. Zkontrolovat, že iss a aud odpovídají tomu, co tato služba očekává.

Teprve pak claimy něco znamenají. Do té doby je payload neověřený řetězec, který dorazil po síti.

Praktické poznámky

  • Tokeny se obvykle posílají v hlavičce HTTP jako Authorization: Bearer <token>, takže payload držte malý. Každý claim, který přidáte, se posílá s každým požadavkem.
  • Podepsaný token zůstává platný, dokud nevyprší. Neexistuje vestavěný způsob, jak ho odvolat, a proto mívají přístupové tokeny krátkou životnost, často v řádu minut, a k získání nových se používá samostatný obnovovací token.
  • Pokud token vypadá poškozeně, nejdřív spočítejte tečky. Dvě tečky a tři části jsou normální podepsaný JWT. Velmi častou příčinou hlášky „invalid signature“ je useknutý token vložený z terminálu.
  • Dekódovat token při ladění, abyste si přečetli jeho expiraci, subjekt nebo role, je naprosto bezpečné a nevyžaduje to žádné tajemství. Přesně k tomu payload slouží.