Il rischio reale dei JWT viene dalla configurazione, non dal formato. Leggere il payload di un token (cosa fa il JWT decoder) è la parte facile: chiedersi se quel token è esposto a uno dei pattern noti di compromissione è ciò che separa un'integrazione production-grade da un incidente in attesa di accadere. Le CVE storiche su librerie JWT diffuse (jsonwebtoken < 8.5, jose < 4, python-jwt, ruby-jwt) sono tutte conseguenza di assunzioni implicite del backend che il tool fa emergere dal solo manifest del token.
I quattro vettori classici verificati dall'audit. (1) alg=none e permutazioni: librerie storicamente vulnerabili accettavano header {"alg":"none"} bypassando la verifica. Il check copre la stringa esatta e tutte le permutazioni di case (None, NONE, nULL, null, alg vuoto, alg numerico, alg mancante). (2) HS256 con weak secret: HMAC con secret condiviso brute-forzabile offline se il secret è una passphrase umana. L'audit prova un dataset curato di 10.000 secret comuni (wallarm/jwt-secrets + SecLists/scraped-JWT-secrets, ricompilato giornalmente) verificando ogni candidato via WebCrypto HMAC-SHA256 in locale. (3) alg confusion attack: il backend è configurato per RS256 ma accetta anche HS256, e usa la public key RSA come secret HMAC. Vettore documentato in CVE storiche (jsonwebtoken pre-8.5). Se fornisci la public key, il tool tenta la verifica HMAC con la pubkey come secret e segnala critical se passa: significa che il backend accetterebbe token attacker-controllati firmati con la sua stessa public key (chiave pubblica per definizione). (4) kid injection / jku-x5u abuse: header che dichiarano la chiave da usare per la verifica. Se il backend non li sanitizza (kid usato come key path o lookup SQL) o non li pinna a domini fidati (jku/x5u), un attaccante che controlla l'input controlla la chiave.
Claim mancanti. RFC 7519 definisce claim opzionali con valore di sicurezza concreto: exp (expiration, scadenza temporale), nbf (not before, ritardo di attivazione), iat (issued at, timestamp di emissione), jti (JWT ID, anti-replay), aud (audience, mitigazione del confused deputy attack), iss (issuer, mitigazione del rogue IdP). Anti-pattern frequente: token senza exp, che vivono per sempre senza possibilità di revoca server-side né rotazione automatica; oppure senza aud, che il service A può inoltrare al service B che lo accetta perché coperto dallo stesso identity provider, anche se non era destinato a lui.
Header esoterici. Due header meno noti che il tool segnala come info: cty (content type, abilita nested JWT - un JWT dentro un JWT, vettore di confusione se il backend lo de-wrappa senza riautenticare), zip (compression: l'header {"zip":"DEF"} abilita la decompressione DEFLATE del payload, ed è stato vettore di attacchi reali via zip bomb su librerie che non limitavano la dimensione output).
Privacy operativa. Il token JWT contiene il payload con i claim utente e l'header con la configurazione di firma: in audit reali non vuoi caricarlo su un servizio terzo, anche con TLS. Qui non parte nessuna richiesta di rete con i dati del token. Il dataset di secret (10.000 entries, ~180 KB raw) viene scaricato dal browser una sola volta al primo utilizzo e cachato in localStorage per le sessioni successive (invalidazione automatica via SHA-256 sul file pubblico). Audit, parsing del JWT, verifica HMAC, brute-force sui secret, lettura della pubkey: tutto in locale via WebCrypto API.
Quando il triage statico è il livello giusto. Casi tipici. (a) Review preliminare di un'API legacy senza accesso al backend: hai catturato un token dal frontend e vuoi capire se la configurazione lato server è solida. (b) Pre-engagement audit per stimare lo sforzo di un eventuale incarico di hardening JWT prima di firmare il preventivo. (c) Screen iniziale prima di un assessment cyber serio: la lista dei high/critical va approfondita con esercizio di exploitability assessment, mappatura dei codepath di verifica nel backend, verifica della whitelist di algoritmi accettati e del pinning JWKS. (d) Self-check periodico per il proprietario dell'API che vuole sapere se l'integrazione è stata fatta a regola d'arte.
Limite di scope. L'audit non sostituisce: una review architetturale del flusso completo (rotazione delle chiavi, refresh token rotation, logout server-side con blacklist jti, audience binding, gestione del kid e jku/x5u in produzione, configurazione del provider OIDC); l'analisi statica del codice di verifica nel backend (whitelist algoritmi, leeway sull'exp, gestione delle eccezioni); lo studio del threat model specifico dell'applicazione. È un triage e si propone come tale.