JSON Web Token spiegati: claim, firme e validazione sicura

Aggiornato:

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:

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ù.

Apri il decodificatore JWT

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.

ClaimNomeSignificato
issEmittenteChi ha creato e firmato il token, di solito un URL
subSoggettoA chi si riferisce il token, di solito un ID utente
audDestinatarioA chi è destinato il token; un destinatario non elencato deve rifiutarlo
expScadenzaDa questo momento in poi il token non deve essere accettato
nbfNon prima diPrima di questo momento il token non deve essere accettato
iatEmissioneQuando è stato creato il token, il che ne indica l’età
jtiID del JWTUn 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.

algMetodoChi può verificarloNote
HS256HMAC con SHA-256 e un segreto condivisoChiunque abbia il segreto — che quindi può anche creare tokenLa chiave deve essere di almeno 256 bit (RFC 7518) e mai una password (RFC 8725).
RS256Firma RSA con SHA-256Chiunque abbia la chiave pubblicaChiavi RSA di almeno 2048 bit. È la scelta predefinita suggerita per gli ID token OpenID Connect.
ES256ECDSA sulla curva P-256 con SHA-256Chiunque abbia la chiave pubblicaChiavi e firme molto più corte rispetto a RSA.
Ed25519EdDSA con il set di parametri Ed25519Chiunque abbia la chiave pubblicaRegistrato nell’ottobre 2025 (RFC 9864), sostituisce il vecchio identificatore EdDSA, meno specifico, che molte librerie usano ancora.
noneNessuna firmaNessuno: il token non è protettoLe 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:

  1. 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.
  2. Verifica la firma con la chiave che appartiene all’emittente previsto.
  3. Controlla che iss sia l’emittente di cui ti fidi e che aud indichi il tuo servizio.
  4. 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.
  5. 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.
  6. 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:

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

  1. RFC 7519 — JSON Web Token (JWT) (May 2015)
  2. RFC 7515 — JSON Web Signature (JWS) (May 2015)
  3. RFC 7516 — JSON Web Encryption (JWE) (May 2015)
  4. RFC 7518 — JSON Web Algorithms (JWA) (May 2015)
  5. RFC 8725 — JSON Web Token Best Current Practices (February 2020)
  6. RFC 9068 — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens (October 2021)
  7. RFC 9700 — Best Current Practice for OAuth 2.0 Security (January 2025)
  8. RFC 9864 — Fully-Specified Algorithms for JOSE and COSE (October 2025)
  9. OpenID Connect Core 1.0 incorporating errata set 2 (December 2023)
  10. OWASP — JSON Web Token Cheat Sheet
  11. OWASP — HTML5 Security Cheat Sheet: Local Storage

Guide correlate