Žetoni JWT pod drobnogledom: varnost, ranljivosti in dobre prakse
Žetoni JWT (JSON Web Token) so de facto standard za avtentikacijo brez stanja (stateless) v sodobnih spletnih aplikacijah. Njihova prilagodljivost pa pogosto pripelje do usodnih napak v konfiguraciji. Ko penetracijski testerji zagledamo JWT, v njem ne vidimo le identifikatorja seje – vidimo kriptografsko površino napada.
Na kratko
- JWT je kodiran, ne šifriran – vsak zahtevek (claim) v vsebini (payload) lahko prebere vsakdo, ki ima žeton.
- Algorithm confusion (zamenjava algoritma RS256 s HS256) in algoritem
noneostajata najresnejši napaki pri preverjanju podpisa, ki ju najdemo v lastnih in starejših implementacijah. - Skrivni ključi HS256 so gesla. Prestreženi žeton lahko napadalec z orodjem Hashcat zlomi brez povezave (offline), ne da bi se cilja še enkrat dotaknil.
- Parametri glave, ki jih pošlje odjemalec, na primer
kidinjku, iz preverjanja podpisa naredijo vstopno točko za vrivanje. - Veljaven podpis ne pove ničesar o veljavnosti zahtevkov –
exp,issinaudpreverjajte pri vsaki zahtevi. - Dostopne žetone (access tokens) hranite v pomnilniku, osvežitvene žetone (refresh tokens) pa v piškotkih z atributi
HttpOnly,SecureinSameSite– nikoli vlocalStorage.
Zgradba žetona JWT: glava, vsebina in podpis
JWT sestavljajo trije deli: glava (header), vsebina (payload) in podpis (signature), med seboj ločeni s piko (.). Penetracijski tester v prestreženem žetonu sprva vidi le dolg niz navidezno naključnih znakov. Primer iz prakse:
eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm9sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ.SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9ZdxEJbwTxRs_6Lb-fZTDpW6lKYNdMyjw45_alSCZ1fypsMWz_2mTpQzil0lOtps5Ei_z7mM7M8gCwe_AGpI53JxduQOaB5HkT5gVrv9cKu9CsW5MS6ZbqYXpGyOG5ehoxqm8DL5tFYaW3lB50ELxi0KsuTKEbD0t5BCl0aCR2MBJWAbN-xeLwEenaqBiwPVvKixYleeDQiBEIylFdNNIMviKRgXiYuAvMziVPbwSgkZVHeEdF5MQP1Oe2Spac-6IfA
Glava in vsebina sta zgolj objekta JSON, kodirana z Base64Url. Samo kodirani sta, ne šifrirani. Vsakdo, ki ima dostop do žetona, lahko te podatke dekodira in prebere.
Vizualni prikaz zgradbe JWT
JWT (tehnično gledano JWS, torej JSON Web Token, »ko je podpisan«) sestavljajo trije deli, ločeni s pikami: Header.Payload.Signature.
{ "alg": "HS256", "typ": "JWT" }{ "sub": "andrej", "role": "hacker" }HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
"secret"
) = MmM0ZGMxM2Y1NWQ2YTY2YTEGlava
Glava vsebuje metapodatke o samem žetonu, predvsem vrsto žetona in kriptografski algoritem, s katerim je zaščiten. Ko dekodiramo prvi del zgornjega žetona, dobimo:
{
"kid": "9136ddb3-cb0a-4a19-a07e-eadf5a44c8b5",
"alg": "RS256"
}
Vsebina (payload)
Vsebina nosi dejanske zahtevke (claims), torej trditve o uporabniku. Pri varnostnem testiranju je prav ona glavna tarča manipulacije. Ko dekodiramo srednji del našega primera, dobimo:
{
"iss": "portswigger",
"exp": 1648037164,
"name": "Andrej Šebeň",
"sub": "andrej",
"role": "pentester",
"email": "andrej@haxoris.com",
"iat": 1516239022
}
Podpis
Ker lahko glavo in vsebino vsakdo zlahka prebere ali spremeni, je varnost vsakega mehanizma, ki temelji na JWT, močno odvisna od kriptografskega podpisa.
Strežnik, ki žeton izda, s skrivnim podpisnim ključem izračuna zgoščeno vrednost glave in vsebine, kodiranih z Base64Url. Ta mehanizem zagotavlja dvoje:
- Ker je podpis izpeljan neposredno iz preostalega dela žetona, se po spremembi enega samega bajta v glavi ali vsebini podpis ne ujema več.
- Brez poznavanja strežnikovega skrivnega podpisnega ključa ne bi smelo biti mogoče ustvariti pravilnega podpisa za ponarejeno glavo ali vsebino.
Kaj hekerji iščejo v žetonu JWT
Ker so žetoni JWT po zasnovi berljivi, jih napadalci ne obravnavajo kot šifrirane podatke, ampak kot dragocen vir za izvidovanje. Z napadalčevega zornega kota se logične napake običajno skrivajo v vsebini. To so ključni zahtevki, ki jih pregledamo najprej:
| Zahtevek | Namen | Kaj preverimo pri testu |
|---|---|---|
iss (Issuer) | Pove, kdo je žeton izdal. | Ali zaledni sistem to res preverja? Sistem, ki sprejema tudi žetone nepooblaščenih tretjih izdajateljev, je ranljiv za napad token confusion (zamenjava žetonov). |
sub (Subject) | Uporabnik ali druga entiteta, ki jo žeton predstavlja; pogosto je to ID uporabnika. | Ranljivost IDOR (insecure direct object reference). Ali lahko vrednost spremenimo v admin ali user_id=1 in obidemo avtorizacijo? |
aud (Audience) | Pove, komu je žeton namenjen. | Ali je mogoče žeton, izdan za eno mikrostoritev, s ponovitvenim napadom (replay) uporabiti pri povsem drugi mikrostoritvi z visokimi pravicami? |
exp (Expiration Time) | Časovni žig Unix, ob katerem žetonu poteče veljavnost. | Ali strežnik potek veljavnosti res uveljavlja? Redno najdemo vmesnike API, ki potekle žetone sprejemajo v nedogled. |
nbf (Not Before) | Čas, pred katerim žetona ni dovoljeno sprejeti. | V logiki preverjanja ga pogosto povsem izpustijo. |
iat (Issued At) | Čas, ko je bil žeton ustvarjen. | Uporaben za izračun življenjske dobe žetonov in za odkrivanje žetonov, ki se nikoli ne zamenjajo. |
Izvidovanje v vsebini žetona: zlata jama razkritih informacij
Ker vsebino vidi vsakdo, ki ima žeton, bi jo morali razvijalci obravnavati kot javno informacijo. Pri testih v zahtevkih po meri pogosto najdemo interne podrobnosti, ki tja ne sodijo:
- Kartiranje notranjega omrežja: zahtevki po meri, kot so
"node_ip": "10.0.4.12","cluster": "prod-us-east-1a"ali"db_host": "internal-db.local", razkrivajo arhitekturo notranjega omrežja in razpone naslovov IP. - Razkritje ogrodja in ponudnika identitete: že poimenovanje zahtevkov izda tehnološki sklad v ozadju. Zahtevki, kot je
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, kažejo na zaledni sistem Microsoft .NET / Entra ID, zato lahko ciljamo na ranljivosti, značilne za ta sklad. - Osebni podatki (PII): nepotrebno razkrivanje e-poštnih naslovov, telefonskih številk, polnih imen ali poštnih naslovov uporabnikov, kar takoj pomeni kršitev Splošne uredbe o varstvu podatkov (GDPR) oziroma zakona CCPA.
- Enumeracija najemnikov in vlog: zahtevki, kot so
"tenant_id": "uuid","is_god_mode": falseali"permissions": ["read:profile"], razkrijejo avtorizacijsko shemo in nam dajo natančna imena parametrov, ki jih izkoristimo za privilege escalation (dvig pravic).
Primer preveč zgovorne vsebine žetona JWT, prestrežene v resničnem okolju:
{
"iss": "auth.internal-corp.local",
"sub": "usr_99812",
"email": "andrej@haxoris.com",
"role": "admin",
"internal_ip": "10.240.12.88",
"db_shard": "shard-04-eu",
"is_admin": true,
"exp": 1748037164
}
Dostopni in osvežitveni žetoni: arhitektura in shranjevanje
Varna implementacija JWT temelji na dveh vrstah žetonov: kratkoživih dostopnih žetonih (bearer tokens) in dolgoživih osvežitvenih žetonih (refresh tokens). Njihovo napačno shranjevanje je ranljivost, ki jo v poročilih navajamo najpogosteje.
Dostopni žetoni (bearer tokens)
- Dostopni žetoni avtorizirajo zahteve API in so namenoma kratkoživi – običajno veljajo od 5 do 15 minut.
- Hranite jih samo v pomnilniku aplikacije. Izogibajte se shrambama
localStorageinsessionStorage, saj lahko žetone napadalcu razkrije vsaka uspešno izrabljena ranljivost XSS.
Osvežitveni žetoni (refresh tokens)
- Osvežitveni žetoni so namenjeni izključno pridobivanju novih dostopnih žetonov po poteku veljavnosti.
- Hranite jih v piškotkih z atributi
HttpOnly,SecureinSameSite. JavaScript tako do njih nima dostopa, kar občutno zmanjša možnost kraje žetonov prek XSS in hkrati pomaga omejiti napade CSRF.
Priročnik penetracijskega testerja: pogosti napadi na JWT
Ko preverjajo spletno aplikacijo, ekipe penetracijskih testerjev običajno preizkusijo naslednje vektorje napada.
A. Napake pri preverjanju podpisa
Številne ranljivosti JWT ne izvirajo iz zlomljene kriptografije, ampak iz nepravilnega preverjanja podpisa. Nekatere knjižnice so v preteklosti sprejemale algoritem none, zato so lahko napadalci podpis v celoti odstranili in poljubno spreminjali zahtevke, na primer uporabniške vloge. Vse večje sodobne knjižnice algoritem none privzeto zavrnejo, vendar se ta napaka še vedno pojavlja v starejših aplikacijah in lastnih implementacijah JWT – zato jo je vredno preveriti vsakič, ko naletite na nestandarden tehnološki sklad.
Druga klasična težava je algorithm confusion. To je ena od ranljivosti JWT z največjim vplivom, zato jo je vredno natančno razumeti.
- RS256 je asimetričen algoritem. Strežnik žetone podpisuje s svojim zasebnim ključem in jih preverja s pripadajočim javnim ključem. Javni ključ je pogosto prosto dostopen – objavljen je na primer na končni točki
/.well-known/jwks.json. - HS256 je simetričen algoritem. Strežnik za podpisovanje in preverjanje žetonov uporablja en sam skupni skrivni ključ.
Napad izkorišča položaj, v katerem strežnik sprejema oba algoritma, ne da bi imel fiksno določeno, katerega pričakuje:
- Napadalec pridobi strežnikov javni ključ – pogosto brez težav, kar s končne točke JWKS.
- Spremeni vsebino žetona – na primer
"role": "user"povzdigne v"role": "admin". - V glavi zamenja
"alg": "RS256"z"alg": "HS256". - Celoten spremenjeni žeton (glavo in vsebino) znova podpiše, pri čemer javni ključ uporabi kot skrivni ključ HS256.
- Strežnik žeton prejme, vidi
alg: HS256in ga preveri z javnim ključem kot skrivnim ključem HMAC. Podpis se ujema in strežnik ponarejeni žeton sprejme.

Temeljni vzrok ni zlomljena kriptografija, ampak to, da strežnik slepo zaupa parametru alg v glavi, ki ga pošlje odjemalec, in mu prepusti odločitev, kako preveriti žeton. Običajno bi sprememba vrednosti alg v glavi podpis razveljavila, saj podpis zajema tako glavo kot vsebino. Napadalec pa znova podpiše celoten spremenjeni žeton, zato je novi podpis po zamenjanem algoritmu povsem veljaven.
Sodobne knjižnice so te napake večinoma odpravile, še vedno pa se pojavljajo v starejših aplikacijah in lastnih implementacijah JWT.
B. Šibki skrivni ključi za podpisovanje
Aplikacije, ki uporabljajo simetrične algoritme, kot je HS256, so varne le toliko, kolikor je varen skrivni ključ, s katerim je žeton podpisan.
Pri penetracijskem testu lahko prestreženi JWT napademo brez vsakršne povezave s ciljno aplikacijo, z orodji, kot sta Hashcat in John the Ripper. Šibki skrivni ključi, kot so imena podjetij, slovarske besede ali predvidljive fraze, pogosto padejo v nekaj minutah. Napadalec lahko nato ustvarja veljavne žetone za katerega koli uporabnika, ne da bi mu bilo treba s ciljno aplikacijo še kdaj stopiti v stik.
C. Vrivanje v parametre glave
Glava JWT lahko vsebuje neobvezne parametre, kot sta kid (Key ID) in jku (JWK Set URL), ki strežniku pomagajo določiti, kateri podpisni ključ naj uporabi pri preverjanju. Ker te vrednosti pošlje odjemalec, lahko zaupanje vanje brez ustreznega preverjanja privede do resnih ranljivosti.
Med pogostimi vektorji napada so:
- prirejanje parametra
jku, tako da kaže na končno točko JWKS pod napadalčevim nadzorom - directory traversal (prehajanje po imenikih) prek parametra
kid, kadar se ključi nalagajo neposredno iz datotečnega sistema - SQL injection (vrivanje SQL) tam, kjer se vrednost
kidz združevanjem nizov vgradi v poizvedbe podatkovne baze
Zrele knjižnice JWT te scenarije obvladajo varno, lastna logika preverjanja pa pri varnostnih testih še naprej prinaša ugotovitve.
D. Napake pri preverjanju zahtevkov
Veljaven podpis še ne pomeni, da je žetonu mogoče zaupati. Aplikacija mora preveriti tudi vsak zahtevek, ki je pomemben za varnost.
Pri penetracijskih testih redno naletimo na vmesnike API, ki:
- sprejemajo potekle žetone, ker ne upoštevajo zahtevka
exp - ne preverjajo predvidenega naslovnika (
aud), zato je mogoče isti žeton ponovno uporabiti pri drugih storitvah - zaupajo poljubnim vrednostim izdajatelja (
iss) - se zanašajo samo na zahtevke pod nadzorom odjemalca, kot so
role,tenantalipermissions, avtorizacije pa na strežniku ne uveljavljajo
Te logične napake imajo v praksi pogosto večji vpliv kot kriptografske šibkosti, saj omogočajo, da se legitimni žetoni uporabijo na načine, ki niso bili predvideni.
Sodobna orodja za varnostno testiranje JWT
Ročno dekodiranje Base64 in prirejanje podpisov vzameta preveč časa. Penetracijski testerji zato te napade avtomatizirajo s specializiranimi orodji:
- JWTAuditor: odlična platforma za varnostno testiranje, ki teče na strani odjemalca in je prilagojena penetracijskim testerjem. Omogoča hiter pregled žetonov, samodejno odkrivanje ranljivosti (na primer preverjanje algoritma
noneali vrivanja v glavo) in napade z grobo silo brez povezave, ne da bi občutljivi žetoni strank končali v telemetriji tretjih oseb. - Burp Suite (razširitev JWT Editor): pri dinamičnem testiranju spletnih aplikacij ga pogosto uporabljamo za sprotno spreminjanje žetonov med prestrezanjem prometa HTTP s posredniškim strežnikom (proxy).
- JWT.io: orodje, ki ga vzdržuje Auth0, je standardna izbira za hitro ročno dekodiranje ter za preverjanje zgradbe in podpisov žetonov med začetnim izvidovanjem.
- JWTLens: lahka alternativa za pregledovanje žetonov, ki na prvo mesto postavlja zasebnost. Žetone dekodira in preverja v celoti v brskalniku z vgrajenim vmesnikom Web Crypto API, zato vaši žetoni in ključi nikoli ne zapustijo vaše naprave. Ker med preverjanjem ne pošlje nobene omrežne zahteve, je idealno orodje za varno preverjanje algoritmov in podpisov.
Dobre prakse za obrambo
Da se vaša aplikacija ne znajde na seznamu kritičnih ugotovitev v poročilu o penetracijskem testu, naj razvojne ekipe uveljavijo te temeljne ukrepe:
- Pričakovani algoritem fiksno določite: nikoli ne dovolite, da bi prejeti žeton narekoval, kako ga boste preverili. Postopek preverjanja izrecno nastavite tako, da pričakuje točno določen algoritem (na primer
RS256), algoritemnonepa brezpogojno zavrnite. - Preverjajte vse zahtevke: veljaven podpis dokazuje le, da žeton ni bil spremenjen, ne pa tudi, da je za uporabnika trenutno veljaven. Pri čisto vsaki zahtevi izrecno preverite
exp(potek veljavnosti),iss(izdajatelj) inaud(naslovnik). - S simetričnimi skrivnimi ključi ravnajte kot z gesli: če vaša arhitektura zahteva simetrično podpisovanje (
HS256), je skrivni ključ dejansko korensko geslo (root). Poskrbite, da ima vsaj 256 bitov entropije, da ga ustvari kriptografsko varen generator naključnih števil in da je varno shranjen v okoljskih spremenljivkah ali upravitelju skrivnosti (secrets manager) – nikoli ga ne zapisujte neposredno v izvorno kodo. - Ne pozabite: kodiranje ni šifriranje. V vsebino JWT nikoli ne shranjujte občutljivih podatkov, osebnih podatkov ali gesel. Kdor žeton prestreže, ga lahko v trenutku dekodira iz Base64.
- Ločite shranjevanje žetonov: kratkoživih dostopnih žetonov nikoli ne shranjujte v
localStoragealisessionStorage, kjer zadostuje ena sama ranljivost XSS, da jih napadalec odtuji. Dostopne žetone hranite izključno v pomnilniku na strani odjemalca, dolgožive osvežitvene žetone pa v varnih piškotkih z atributomaHttpOnlyinSameSite.
Napredno utrjevanje: vzorec phantom token
V okoljih z visokimi varnostnimi zahtevami je najučinkovitejša obramba pred krajo žetonov na strani odjemalca ta, da JWT odjemalskemu delu aplikacije (frontend) sploh nikoli ne izpostavite. Sodobne arhitekture v velikih podjetjih pogosto uvedejo vzorec »phantom token«, ki poteka v teh korakih:
- Avtorizacijski strežnik odjemalski aplikaciji namesto JWT izda naključen, neprozoren (opaque) niz seje.
- Ko odjemalec pošlje zahtevo API, ji priloži ta neprozorni niz.
- Prehod API (API gateway) zahtevo prestreže, neprozorni žeton preveri pri avtorizacijskem strežniku (ali v shrambi sej) in ga zamenja s podpisanim JWT z vsemi zahtevki.
- Prehod API nato pravi JWT posreduje notranjim mikrostoritvam.
Pogosta vprašanja o varnosti JWT
01Ali je JWT šifriran?
Ne. Glava in vsebina standardnega podpisanega žetona JWT (JWS) sta samo kodirani z Base64Url, ne šifrirani. Vsakdo, ki ima žeton, lahko dekodira in prebere vse zahtevke v njem. Podpis varuje celovitost, ne zaupnosti, zato občutljivi in osebni podatki (PII) ne sodijo v vsebino JWT.
02Kaj je algorithm confusion pri JWT?
Do napada algorithm confusion (zamenjava algoritma) pride, kadar strežnik dovoli, da o načinu preverjanja podpisa odloča parameter alg v glavi samega žetona. Napadalec vzame strežnikov javni ključ RSA, v glavi zamenja RS256 s HS256, spremeni vsebino in celoten žeton znova podpiše, pri čemer ta javni ključ uporabi kot skrivni ključ HMAC. Strežnik, ki sprejema oba algoritma, ponaredek uspešno preveri. Rešitev je, da pričakovani algoritem fiksno določite na strežniku.
03Kje v brskalniku hraniti dostopne in osvežitvene žetone?
Kratkožive dostopne žetone hranite samo v pomnilniku aplikacije. Dolgožive osvežitvene žetone shranite v piškotke z atributi HttpOnly, Secure in SameSite, tako da jih JavaScript ne more prebrati. Nobenega od obeh žetonov nikoli ne shranjujte v localStorage ali sessionStorage, kjer zadostuje ena sama ranljivost XSS, da ju napadalec odtuji.
04Kako napadalci zlomijo skrivne ključe HS256?
Prestreženi žeton HS256 je mogoče napasti povsem brez povezave (offline) z orodji, kot sta Hashcat in John the Ripper, brez kakršnega koli nadaljnjega stika s ciljno aplikacijo. Šibki skrivni ključi, kot so imena podjetij, slovarske besede ali predvidljive fraze, pogosto padejo v nekaj minutah, nato pa lahko napadalec izdaja veljavne žetone za katerega koli uporabnika. Simetrični skrivni ključi potrebujejo vsaj 256 bitov entropije iz kriptografsko varnega generatorja.
05Ali veljaven podpis pomeni, da lahko žetonu JWT zaupamo?
Ne. Veljaven podpis dokazuje le, da žeton ni bil spremenjen. Aplikacija mora kljub temu preveriti vsak zahtevek, pomemben za varnost: exp za potek veljavnosti, iss za izdajatelja in aud za predvidenega naslovnika. Nikoli se ne sme zanašati na zahtevke, ki jih pošlje odjemalec, na primer role ali permissions, ne da bi avtorizacijo uveljavila na strežniku.
Kako lahko pomaga Haxoris
Varnost žetonov le redko pade zaradi kriptografije – pade zaradi konfiguracije. Haxoris vam pomaga te vrzeli najti, preden jih najde napadalec:
- penetracijski testi spletnih aplikacij in API-jev s posebnim poudarkom na postopkih avtentikacije in avtorizacije
- pregled logike izdajanja in preverjanja žetonov JWT ter upravljanja ključev, vključno z obravnavo JWKS in rotacijo ključev
- preverjanje moči simetričnih skrivnih ključev za podpisovanje brez povezave s ciljnim sistemom (offline)
- pregled zasnove shranjevanja žetonov, trajanja sej in arhitekture osvežitvenih žetonov
Sklep
Žetoni JWT niso nevarni že po zasnovi. Nevarni postanejo v trenutku, ko aplikacija žetonu prepusti odločitev, kako naj ga preveri, ali ko veljaven podpis obravnava kot nadomestek za avtorizacijo. Algoritem fiksno določite, preverjajte vsak zahtevek, s skrivnimi ključi ravnajte kot s korenskimi gesli in žetonov ne hranite v shrambi brskalnika – in večina ugotovitev iz tega članka bo iz vašega naslednjega poročila o penetracijskem testu izginila.
Viri in priporočeno branje
Metodologije in tehnične podrobnosti v tem članku se zgledujejo po naslednjih uveljavljenih virih s področja varnosti in so po njih prirejene:
- PortSwigger Web Security Academy. JWT Security Vulnerabilities. Podrobna razčlenitev napak pri preverjanju podpisa in vrivanja v glavo. portswigger.net/web-security/jwt
- InfoSec Writeups. JWT Pentesting: A Journey from Token to Takeover. Praktične študije primerov izrabe napačnih konfiguracij žetonov. infosecwriteups.com
- Auth0 / JWT.io. Introduction to JSON Web Tokens. Temeljna dokumentacija o zgradbi žetonov in standardu RFC 7519. jwt.io/introduction
- JWTAuditor. Security Testing Tool. Orodje za prirejanje vsebine žetonov in samodejno odkrivanje ranljivosti. jwtauditor.com