Vai al contenuto
Toova
Tutti gli strumenti

Cos'è JWT e Come Decodificarlo in Sicurezza

Toova

I JSON Web Token appaiono ovunque nello sviluppo web moderno — header di autenticazione, flussi OAuth, chiavi API, gestione delle sessioni. Se hai lavorato con qualsiasi API che richiede un token Bearer, hai già usato un JWT. Ma la maggior parte degli sviluppatori li tratta come blob opachi e li esamina raramente dall'interno. Capire la struttura di un JWT, cosa significa ogni parte e come decodificarne uno senza fare errori di sicurezza richiede circa quindici minuti. Questa guida copre tutto.

L'Anatomia di un JWT

Ogni JWT è una stringa di tre segmenti codificati in Base64URL separati da punti:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzQ3MDAwMDAwLCJpYXQiOjE3NDY5OTY0MDAsInNjb3BlIjoicmVhZDp1c2VycyJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Il formato è header.payload.firma. Ogni segmento codifica informazioni diverse.

L'Header

Il primo segmento è l'header. Decodificato, l'esempio sopra diventa:

{
  "alg": "RS256",
  "typ": "JWT"
}

alg specifica l'algoritmo usato per firmare il token — RS256 significa RSA con SHA-256. typ identifica il tipo di token. Questi due campi sono il minimo; alcuni token includono anche kid (key ID) per dire al verificatore quale chiave pubblica usare quando ci sono più chiavi in rotazione.

Il Payload

Il secondo segmento è il payload — i dati effettivi. Decodificato:

{
  "sub": "user_123",
  "iss": "https://auth.example.com",
  "aud": "https://api.example.com",
  "exp": 1747000000,
  "iat": 1746996400,
  "scope": "read:users"
}

Il payload contiene i claim: affermazioni sull'entità che il token rappresenta. Alcuni claim sono standardizzati (chiamati claim registrati); altri sono specifici dell'applicazione (chiamati claim privati). Il payload è codificato in Base64URL, non cifrato. Chiunque ottenga la stringa del token può leggere ogni claim al suo interno.

La Firma

Il terzo segmento è la firma. Viene prodotta prendendo l'header e il payload codificati, concatenandoli con un punto, poi firmando il risultato con la chiave segreta o la chiave privata specificata in alg:

firma = sign(
  base64url(header) + "." + base64url(payload),
  secretOrPrivateKey
)

La firma permette a qualsiasi parte con la chiave pubblica corrispondente (o la chiave segreta, per gli algoritmi simmetrici) di verificare che il token sia stato emesso da una fonte affidabile e che né l'header né il payload siano stati modificati dopo l'emissione. Un singolo carattere modificato nel payload invalida completamente la firma.

Come Decodificare un JWT

Decodificare è diverso dal verificare. Decodificare legge semplicemente i dati all'interno di un token. Verificare conferma che il token è autentico e non scaduto. Dovresti sempre verificare nel codice di produzione; decodificare da solo è utile per il debug.

Passo 1: Dividi il token

Dividi la stringa JWT sul carattere .. Otterrai tre segmenti. Se ne ottieni meno di tre, il token è malformato.

Passo 2: Decodifica header e payload

Decodifica in Base64URL ciascuno dei primi due segmenti. Base64URL è come Base64 standard ma usa - invece di + e _ invece di /, senza caratteri di padding. Una volta decodificato, puoi analizzare ogni segmento come JSON.

Passo 3: Analizza e ispeziona

Leggi i claim. Controlla exp (scadenza) rispetto al timestamp Unix corrente. Guarda iss e aud per confermare che il token sia destinato alla tua applicazione. Non fidarti di nessun claim finché non hai verificato la firma.

Per l'ispezione rapida durante lo sviluppo, usa il JWT Decoder. Gira interamente nel tuo browser — il tuo token non lascia mai il tuo dispositivo. Puoi anche decodificare i segmenti base64 manualmente con l'encoder/decoder Base64 se vuoi vedere i byte grezzi. Per verificare le firme negli script di test, il generatore HMAC ti permette di riprodurre le firme HS256 senza scrivere codice.

Passo 4: Verifica la firma nel codice

In produzione, verifica sempre la firma usando una libreria costruita per il tuo linguaggio. Non implementare la verifica della firma a mano. Le opzioni popolari includono jose o jsonwebtoken per Node.js, PyJWT per Python, golang-jwt/jwt per Go e nimbus-jose-jwt per Java.

Passa sempre l'algoritmo esplicitamente. Non lasciare mai che la libreria deduca l'algoritmo dall'header del token.

Claim Standard (Claim Registrati)

RFC 7519 definisce un insieme di nomi di claim registrati. Non sono obbligatori, ma quando presenti devono seguire la semantica standard.

iss — Emittente

L'entità che ha creato e firmato il token. Di solito un URL che identifica il server di autorizzazione (ad esempio, https://auth.example.com). I verificatori dovrebbero controllare che iss corrisponda a un valore atteso e rifiutare i token di emittenti sconosciuti.

sub — Soggetto

Il principale che il token rappresenta — tipicamente un ID utente, il nome di un account di servizio o un identificatore di dispositivo. Il valore deve essere univoco nel contesto dell'emittente. La tua applicazione usa sub per identificare a quale utente appartiene la richiesta.

aud — Audience

Il/i destinatario/i previsto/i del token. Se l'identificatore della tua API è https://api.example.com, i token emessi per altri servizi dovrebbero essere rifiutati. Non validare aud permette agli attaccanti di riutilizzare un token ottenuto da un servizio presso un altro servizio.

exp — Tempo di Scadenza

Un timestamp Unix dopo il quale il token non deve essere accettato. Valida sempre exp. Un token senza scadenza vive effettivamente per sempre — se viene rubato, l'attaccante ha accesso indefinito.

iat — Emesso Alle

Il timestamp Unix quando il token è stato creato. Utile per rilevare token che sono tecnicamente non scaduti ma sospettosamente vecchi. Puoi usare iat per applicare un'età massima del token indipendente da exp.

nbf — Non Prima Di

Il timestamp Unix prima del quale il token non deve essere accettato. Meno comune di exp, ma utile quando si emettono token in anticipo — ad esempio, un'attività pianificata che non dovrebbe iniziare fino a un momento specifico.

jti — ID JWT

Un identificatore univoco per il token. Permette agli emittenti di prevenire gli attacchi di replay dei token archiviando i valori jti usati e rifiutando i token con ID duplicati. Necessario quando hai bisogno di token monouso, come i link di reset della password.

Vulnerabilità di Sicurezza

Le implementazioni JWT hanno una storia di vulnerabilità sottili. Queste sono le più importanti da capire prima di distribuire codice che accetta JWT.

L'Attacco "alg: none"

Alcune librerie JWT dei primi giorni dello standard accettavano un token che specificava "alg": "none" nel suo header. Un attaccante poteva prendere qualsiasi token valido, sostituire l'algoritmo con none, rimuovere la firma, e la libreria lo accettava come completamente valido — nessuna firma richiesta.

La soluzione: specifica sempre l'algoritmo atteso esplicitamente quando chiami la tua funzione di verifica. Tratta qualsiasi token che dichiara alg: none come non valido. La maggior parte delle librerie moderne ha affrontato questo problema, ma vale la pena confermarlo nei default della libreria prima di andare in produzione.

Confusione di Algoritmo (Downgrade da RS256 a HS256)

Alcune librerie che supportavano entrambi gli algoritmi usavano il campo alg nell'header del token per decidere come verificare. Un attaccante poteva cambiare l'algoritmo in HS256, poi firmare il token usando la chiave pubblica del server come segreto HMAC. Il server, vedendo HS256, verificherebbe rispetto alla chiave pubblica e accetterebbe il token falsificato.

La soluzione è la stessa: blocca l'algoritmo nel tuo codice di verifica. Non lasciare mai che l'header del token influenzi quale algoritmo è usato per la verifica.

Accettare Token Scaduti

Non validare exp è un'omissione comune — a volte introdotta quando gli sviluppatori aggiungono un periodo di grazia che cresce senza limiti, o quando il percorso del codice di validazione viene bypassato in un ramo del codice. Tratta un token scaduto come un token mancante: rifiutalo con un 401 e richiedi al client di ri-autenticarsi o usare un token di aggiornamento.

Dati Sensibili nel Payload

Il payload è codificato in Base64URL, non cifrato. Chiunque intercetti il token può decodificarlo. Non mettere password, numeri di carta di credito, numeri di previdenza sociale o altri dati sensibili nel payload. Se hai bisogno di trasmettere claim sensibili in sicurezza, usa un token JWE (JSON Web Encryption), che cifra il payload. I JWT firmati con JWS (il caso comune) garantiscono solo l'autenticità, non la riservatezza.

Validazione dell'Audience Mancante

Saltare la validazione di aud significa che un token emesso per il Servizio A può essere riprodotto al Servizio B — purché entrambi i servizi condividano la stessa chiave di firma o si fidino dello stesso emittente. Nelle architetture a più servizi, valida sempre che l'audience del token corrisponda all'identificatore del tuo servizio.

Migliori Pratiche per il 2026

Scegli RS256 per la Maggior Parte delle Applicazioni

RS256 usa una chiave privata per firmare e una chiave pubblica per verificare. Solo il server di autorizzazione possiede la chiave privata. Qualsiasi servizio può verificare i token usando la chiave pubblica, che può essere pubblicata apertamente (spesso tramite un endpoint JWKS). Se un singolo servizio è compromesso, gli attaccanti acquisiscono la capacità di verificare i token — ma non di forgerne di nuovi.

HS256 usa un singolo segreto condiviso sia per la firma che per la verifica. Qualsiasi servizio che può verificare i token può anche crearli. In una configurazione a microservizi, condividere il segreto tra molti servizi aumenta il raggio dell'esplosione di una violazione.

Mantieni i Token di Accesso a Vita Breve

Non esiste un meccanismo di revoca integrato per i JWT — una volta emesso, un token è valido fino alla sua scadenza (a meno che non si implementi una blocklist, che reintroduce lo stato lato server). I tempi di scadenza brevi (da 15 minuti a 1 ora) limitano la finestra di danno se un token viene rubato. Abbina i token di accesso con token di aggiornamento a lunga durata archiviati in cookie HttpOnly sicuri, non in localStorage.

Usa la Rotazione dei Token di Aggiornamento

Quando un client usa un token di aggiornamento per ottenere un nuovo token di accesso, emetti un nuovo token di aggiornamento e invalida quello vecchio. In questo modo, un token di aggiornamento rubato viene rilevato la prossima volta che il client legittimo tenta di usare l'originale: il server vede un token di aggiornamento riutilizzato e può revocare l'intera sessione. Questo schema è descritto in RFC 6819 ed è ampiamente supportato dai moderni server di autorizzazione.

Pubblica un Endpoint JWKS

Un endpoint JSON Web Key Set (JWKS) (/.well-known/jwks.json per convenzione) pubblica le tue chiavi pubbliche di firma in un formato standard. Gli altri servizi possono recuperare il JWKS e verificare i token senza ricevere le chiavi fuori banda. Questo rende anche la rotazione delle chiavi semplice: aggiungi la nuova chiave al JWKS, inizia a firmare con essa, poi rimuovi la vecchia chiave una volta scaduti tutti i token esistenti.

Ruota Periodicamente le Chiavi

Anche se la tua chiave privata non viene mai compromessa, ruotare periodicamente le chiavi limita la finestra durante la quale una chiave rubata potrebbe essere usata per forgere token. Usa kid negli header JWT per referenziare la chiave di firma, in modo che i verificatori possano selezionare la chiave pubblica giusta dal tuo JWKS senza rompere i token esistenti durante una rotazione.

Valida Ogni Claim

Controlla iss, aud, exp e nbf ad ogni richiesta. Non saltare nessuno di questi perché sembrano ridondanti nella tua configurazione — le condizioni che li rendono superflui oggi sono esattamente le condizioni che cambiano durante un incidente.

JWT vs. Cookie di Sessione

I JWT e le sessioni lato server risolvono lo stesso problema — persistere lo stato di autenticazione attraverso HTTP stateless — ma con compromessi diversi.

I JWT sono autonomi. Il server non ha bisogno di interrogare un database per validare una richiesta. Questo li rende attraenti per i sistemi distribuiti e le API consumate da client mobili o di terze parti. Lo svantaggio è che la revoca richiede una blocklist (che reintroduce lo stato lato server) o tollerare la durata residua di un token compromesso.

I cookie di sessione sono validati lato server. Il server archivia lo stato della sessione e può revocare l'accesso istantaneamente eliminando la sessione. I cookie con i flag HttpOnly e Secure sono protetti dall'accesso JavaScript e dall'intercettazione di rete. Lo svantaggio è che ogni richiesta richiede una ricerca nel database, che può diventare un collo di bottiglia su scala.

Per le applicazioni web tradizionali con pagine server-rendered e un singolo backend, i cookie di sessione rimangono una scelta solida e più semplice. Per le API consumate da più client, architetture a microservizi e app mobili, i JWT con scadenza breve e rotazione del token di aggiornamento sono l'opzione più pratica.

Decodifica il Tuo Token

Il modo più veloce per ispezionare un JWT è incollarlo nel JWT Decoder. Divide il token, decodifica l'header e il payload e rende i claim in un formato leggibile — tutto senza inviare il tuo token da nessuna parte. Se hai bisogno di codificare o decodificare stringhe Base64URL grezze manualmente, l'encoder/decoder Base64 gestisce entrambe le varianti standard e URL-safe. Per verificare le firme HMAC negli script di test, il generatore HMAC ti permette di riprodurre una firma HS256 e confrontarla con ciò che contiene il tuo token.

Per la specifica completa, vedi RFC 7519 (JWT) e gli esempi interattivi su jwt.io.