JSON Web Token spiegati: claim, firme e validazione sicura
Un JSON Web Token, o JWT, è un breve testo che riporta alcune informazioni su un utente o su una richiesta — chi l’ha emesso, a chi si riferisce, quando scade — insieme a una firma che permette al destinatario di verificare che nessuno le abbia modificate. Le API accettano i JWT come token di accesso, i sistemi di autenticazione li emettono come ID token e molte applicazioni web li usano come token di sessione. Questa guida spiega cosa contiene un JWT, in cosa differiscono gli algoritmi di firma, cosa deve verificare un server prima di fidarsi di un token, quali attacchi sfruttano i controlli saltati e come gestire scadenza e conservazione.
Cos’è un JWT
Il formato è definito nell’RFC 7519, pubblicato nel 2015. Un JWT è un insieme di claim — affermazioni come «il soggetto è user-4821» — codificati in JSON e poi firmati oppure cifrati. Quasi tutti i JWT in uso sono firmati, come JSON Web Signature (JWS, RFC 7515): chiunque può leggerli, ma nessuno senza la chiave può modificarli senza che la cosa venga notata. I JWT cifrati (JWE, RFC 7516) ne nascondono anche il contenuto e sono molto meno comuni.
Due standard definiscono i JWT che è più probabile incontrare. OpenID Connect, il livello di autenticazione usato dai pulsanti «Accedi con…», emette un ID token che deve essere un JWT firmato. Il profilo JWT per i token di accesso OAuth 2.0 (RFC 9068, 2021) definisce come si presenta un token di accesso a un’API quando è un JWT, anche se i token di accesso di molti provider sono nati prima di questo profilo e non lo seguono alla lettera.
Le tre parti
Un JWT firmato è formato da tre parti di testo Base64URL unite da punti: un header, un payload e una firma. Ecco un token completo, diviso in corrispondenza dei punti:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Header: {"alg":"HS256","typ":"JWT"} — l’algoritmo di firma e il tipo di token.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyLTQ4MjEiLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImlhdCI6MTc5MTk2ODQwMCwiZXhwIjoxNzkxOTY5MzAwLCJqdGkiOiJhOTFjMmU3ZiJ9Payload: emittente https://auth.example.com, soggetto user-4821, destinatario https://api.example.com, emesso al tempo 1791968400, scadenza al tempo 1791969300, ID a91c2e7f.yQ_-tR1KNYT8HL6voHLYDvp5GY6EWmR33VMZ8BbD1usFirma: un HMAC-SHA256 delle prime due parti, calcolato con una chiave segreta. Sono dati binari, non JSON.
Base64URL è il normale Base64 con due caratteri sostituiti perché il risultato sia sicuro negli URL (- e _ al posto di + e /); nei JWT, inoltre, il padding = finale viene eliminato. È una codifica, non una cifratura: chiunque può riconvertire le prime due parti in JSON.
La firma è calcolata sul testo esatto dell’header e del payload codificati, uniti dal punto che li separa. Basta cambiare un solo carattere dell’uno o dell’altro — spostare più avanti la scadenza, indicare come soggetto qualcun altro — e la firma non corrisponde più.
I claim standard
L’RFC 7519 definisce sette claim registrati. Nello standard di base sono tutti facoltativi; i protocolli costruiti su di esso ne rendono obbligatori alcuni.
| Claim | Nome | Significato |
|---|---|---|
iss | Emittente | Chi ha creato e firmato il token, di solito un URL |
sub | Soggetto | A chi si riferisce il token, di solito un ID utente |
aud | Destinatario | A chi è destinato il token; un destinatario non elencato deve rifiutarlo |
exp | Scadenza | Da questo momento in poi il token non deve essere accettato |
nbf | Non prima di | Prima di questo momento il token non deve essere accettato |
iat | Emissione | Quando è stato creato il token, il che ne indica l’età |
jti | ID del JWT | Un identificatore univoco, utile contro gli attacchi di replay e per la revoca |
I tre claim temporali (exp, nbf e iat) sono timestamp Unix: numeri di secondi, di norma interi, trascorsi dal 1° gennaio 1970 UTC. Nell’esempio, 1791968400 corrisponde al 14 ottobre 2026 alle 09:00 UTC e 1791969300 a quindici minuti dopo. L’RFC 7519 consente a un server di concedere un piccolo margine di tolleranza per gli orologi non sincronizzati, «di solito non più di qualche minuto».
Un ID token OpenID Connect deve contenere iss, sub, aud, exp e iat. Un token di accesso secondo l’RFC 9068 deve contenere iss, exp, aud, sub, client_id, iat e jti, e riportare nell’header il tipo at+jwt. Tutto il resto del payload — un nome, un indirizzo email, dei ruoli — è definito dall’applicazione.
Algoritmi di firma
Il valore alg nell’header indica l’algoritmo. Gli algoritmi si dividono in due gruppi: a segreto condiviso, in cui la stessa chiave firma e verifica, e a coppia di chiavi, in cui una chiave privata firma e una chiave pubblica verifica.
| alg | Metodo | Chi può verificarlo | Note |
|---|---|---|---|
HS256 | HMAC con SHA-256 e un segreto condiviso | Chiunque abbia il segreto — che quindi può anche creare token | La chiave deve essere di almeno 256 bit (RFC 7518) e mai una password (RFC 8725). |
RS256 | Firma RSA con SHA-256 | Chiunque abbia la chiave pubblica | Chiavi RSA di almeno 2048 bit. È la scelta predefinita suggerita per gli ID token OpenID Connect. |
ES256 | ECDSA sulla curva P-256 con SHA-256 | Chiunque abbia la chiave pubblica | Chiavi e firme molto più corte rispetto a RSA. |
Ed25519 | EdDSA con il set di parametri Ed25519 | Chiunque abbia la chiave pubblica | Registrato nell’ottobre 2025 (RFC 9864), sostituisce il vecchio identificatore EdDSA, meno specifico, che molte librerie usano ancora. |
none | Nessuna firma | Nessuno: il token non è protetto | Le implementazioni non devono accettarlo per impostazione predefinita (RFC 7518). |
HS256 è adatto a un singolo servizio che emette e verifica i propri token. Quando anche altri servizi devono verificarli — più API, o dei partner — è più indicata una coppia di chiavi: gli altri ricevono solo la chiave pubblica, che non può essere usata per creare token.
Decodificare non è verificare
Chiunque può scrivere un JWT con un payload qualsiasi; decodificarlo, come fa il decodificatore di nTools, mostra solo ciò che afferma. Un token dimostra qualcosa solo dopo che il server che lo riceve ha eseguito questi controlli, che seguono l’RFC 7519 e l’RFC 8725, le best practice correnti dell’IETF per i JWT:
- Stabilisci nella configurazione del server quali algoritmi sono accettati e rifiuta tutti gli altri. Non lasciare mai che sia l’alg nell’header del token a decidere come verificarlo, e usa ogni chiave con un solo algoritmo.
- Verifica la firma con la chiave che appartiene all’emittente previsto.
- Controlla che iss sia l’emittente di cui ti fidi e che aud indichi il tuo servizio.
- Esigi che siano presenti exp e gli altri claim da cui dipende il tuo servizio, e confronta exp e nbf con l’ora attuale, con un margine di tolleranza di qualche minuto al massimo. Altrimenti un token senza exp non scadrebbe mai.
- Dove è definito un tipo di token, controlla typ — per esempio at+jwt per i token di accesso — così che un tipo di token non possa essere spacciato per un altro.
- Tratta i campi dell’header kid, jku e x5u come input non affidabile: cerca kid nel tuo elenco di chiavi e prendi le chiavi solo dal set di chiavi configurato per l’emittente o da indirizzi che hai autorizzato in anticipo.
In pratica questi controlli spettano a una libreria JWT mantenuta attivamente e configurata con emittente, destinatario e algoritmi previsti, non a codice scritto a mano.
Gli attacchi classici
La maggior parte delle vulnerabilità dei JWT non sono debolezze della crittografia, ma controlli che un server ha saltato. L’RFC 8725 descrive i seguenti:
- alg none: l’attaccante imposta none nell’header ed elimina la firma. Un server che si fida dell’header accetta un token che chiunque avrebbe potuto scrivere.
- Confusione delle chiavi: un server si aspetta RS256, ma l’attaccante invia un token marcato HS256, firmato con la chiave pubblica del server stesso come se fosse un segreto HMAC. Una libreria che sceglie l’algoritmo in base all’header lo verifica allora con quella stessa chiave pubblica — e il controllo va a buon fine.
- Segreti deboli: un segreto HS256 corto, o che è una password, si può trovare tirando a indovinare offline, partendo da un solo token intercettato, senza alcun contatto con il server. Con il segreto, l’attaccante può firmare qualsiasi token.
- Sostituzione: un token valido emesso per un servizio, o per uno scopo, viene presentato a un altro — un ID token usato come token di accesso, o un token per un’API usato su un’altra.
I controlli della sezione precedente li neutralizzano tutti: un elenco fisso di algoritmi, con ogni chiave legata a un solo algoritmo, ferma i primi due, un segreto casuale di almeno 256 bit il terzo, e il controllo di aud e typ l’ultimo.
Il payload è pubblico
Una firma protegge dalle modifiche, non dalla lettura. I token viaggiano negli header delle richieste (e a volte, incautamente, negli URL) e finiscono nei log, nella memoria del browser e negli header Referer, e chiunque ne veda uno può decodificarlo. Password, segreti e dati personali di cui il destinatario non ha bisogno non dovrebbero stare in un payload.
Quando i claim devono viaggiare con il token e restare riservati, il token può essere cifrato come JWE, che ha cinque parti invece di tre. L’alternativa suggerita da OWASP è spesso più semplice: tenere i dati sul server e dare al client un token di riferimento opaco, che da solo non significa nulla.
Scadenza, revoca e conservazione
Un JWT firmato è valido fino a exp, ovunque si trovi: il server non ha bisogno di cercarlo da nessuna parte, ed è questo che rende i JWT veloci da verificare, ma anche il motivo per cui un token rubato continua a funzionare. Tempi di scadenza brevi limitano i danni. Per ritirare un token prima, il server deve comunque mantenere un certo stato — OWASP descrive una denylist basata sui claim jti e iss; una voce può essere rimossa una volta superato l’exp del token.
Le applicazioni che hanno bisogno di sessioni più lunghe affiancano a un token di accesso di breve durata un refresh token, usato per ottenerne di nuovi. Le best practice di sicurezza di OAuth 2.0 (RFC 9700, gennaio 2025) richiedono che i refresh token emessi ai client pubblici — tipicamente app per browser e per dispositivi mobili, che non possono custodire un segreto — siano vincolati al mittente, cioè legati crittograficamente a quel client, oppure ruotati: sostituiti da uno nuovo a ogni utilizzo, invalidando quello vecchio.
Nel browser, OWASP sconsiglia di conservare gli identificatori di sessione in localStorage, perché basta una sola vulnerabilità di cross-site scripting per leggere tutto ciò che vi è memorizzato. Un cookie contrassegnato come HttpOnly non può essere letto in alcun modo da JavaScript; dovrebbe però essere anche Secure e SameSite, e uno script iniettato nella pagina può comunque inviare richieste che lo includono.
Domande frequenti
Un JWT è cifrato?
Di solito no. La maggior parte dei JWT è firmata, e header e payload sono solo codificati in Base64URL, quindi chiunque abbia il token può leggerli. Solo un JWE, con cinque parti, ne nasconde il contenuto.
Posso leggere un JWT senza la chiave?
Sì, a meno che non sia un JWE cifrato. In un JWT firmato la chiave serve per creare o verificare la firma, non per leggere header e payload.
Per quanto tempo dovrebbe essere valido un JWT?
Gli standard non fissano un valore unico. Il consiglio abituale è il più breve possibile compatibilmente con l’applicazione, con refresh token per le sessioni più lunghe; così un token perso resta utilizzabile solo per poco tempo.
Conviene usare i JWT per le sessioni di un sito web?
Non necessariamente. Un classico ID di sessione in un cookie può essere revocato subito sul server; un JWT evita una consultazione a ogni richiesta, ma non può essere ritirato prima della scadenza senza mantenere uno stato aggiuntivo.
È sicuro incollare un token nel decodificatore di nTools?
Il token viene decodificato nel tuo browser e non viene inviato da nessuna parte. Anche così, è meglio usare un token scaduto o di test: un token valido è una credenziale.
Precedente della serie ← Funzioni di hash spiegate: SHA-256, checksum e hashing delle password
Riferimenti
- RFC 7519 — JSON Web Token (JWT) (May 2015)
- RFC 7515 — JSON Web Signature (JWS) (May 2015)
- RFC 7516 — JSON Web Encryption (JWE) (May 2015)
- RFC 7518 — JSON Web Algorithms (JWA) (May 2015)
- RFC 8725 — JSON Web Token Best Current Practices (February 2020)
- RFC 9068 — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens (October 2021)
- RFC 9700 — Best Current Practice for OAuth 2.0 Security (January 2025)
- RFC 9864 — Fully-Specified Algorithms for JOSE and COSE (October 2025)
- OpenID Connect Core 1.0 incorporating errata set 2 (December 2023)
- OWASP — JSON Web Token Cheat Sheet
- OWASP — HTML5 Security Cheat Sheet: Local Storage
Guide correlate
- Indirizzi IP spiegati: IPv4, IPv6 e gli intervalli riservati
- Sottoreti e CIDR spiegati: prefissi, maschere e host utilizzabili
- Record DNS spiegati: A, AAAA, CNAME, MX, TXT e NS
- Un sito è down o il problema è solo tuo? Come verificarlo, passo per passo
- Codici di stato HTTP ed errori di Cloudflare: cosa significano 403, 404, 429, 500, 503 e 521
- Timestamp Unix e fusi orari spiegati
- Contare le date: giorni tra due date, anni bisestili e settimane ISO
- Formati immagine spiegati: JPEG, PNG, WebP, AVIF e HEIC
- Robustezza delle password spiegata: lunghezza, entropia e passphrase
- Funzioni di hash spiegate: SHA-256, checksum e hashing delle password
- Gli UUID spiegati: v4, v7 e come scegliere la versione giusta
- I codici QR spiegati: capacità, correzione degli errori e dimensioni di stampa
- Percentuali e IVA spiegate: aggiungere, scorporare e cumulare
- Errori comuni nel JSON e come correggerli
- Modificare i PDF nel browser: cosa conservano unione, divisione e conversione