JWT (JSON Web Token) è un formato compatto per trasferire dati tra parti come oggetto JSON protetto da una firma digitale. Il token può essere firmato utilizzando HMAC (chiave simmetrica) o RSA/ECDSA (coppia asimmetrica), garantendo l'integrità e l'autenticità dei dati. Secondo IETF RFC 7519, 2015, JWT è utilizzato in milioni di applicazioni per autenticazione, scambio sicuro di claims e come formato ID Token in OpenID Connect.
Punti Chiave
JSON Web Token (JWT) è uno standard aperto (RFC 7519) che definisce un modo compatto e autocontenuto per trasmettere informazioni tra parti come oggetto JSON. Le informazioni in JWT sono chiamate claims — dichiarazioni sul soggetto (utente) e attributi aggiuntivi. Ogni claim è una coppia chiave-valore: identificatore utente, ruolo, tempo di scadenza, emittente.
JWT è detto autocontenuto perché tutte le informazioni necessarie per la verifica sono all'interno del token stesso. Il server non deve accedere a un database o archivio esterno per verificare la validità del token — basta controllare la firma. Questa proprietà rende JWT ideale per sistemi distribuiti e architetture a microservizi, dove più servizi devono autenticare richieste senza un archivio di sessioni condiviso.
Secondo Auth0, 2025, oltre il 65% delle applicazioni mobili e web utilizza JWT come formato token principale per l'autenticazione API, superando i token opachi e gli identificatori di sessione.
JWT è composto da tre parti separate da punti: header.payload.signature. Ogni parte è un JSON codificato in Base64url. Esaminiamo ciascuna parte in dettaglio.
Header contiene due campi obbligatori: alg (algorithm — algoritmo di firma) e typ (type — tipo di token, sempre “JWT”). L'algoritmo può essere simmetrico (HS256 — HMAC con SHA-256) o asimmetrico (RS256 — RSA con SHA-256, ES256 — ECDSA con P-256). Gli algoritmi asimmetrici sono preferibili perché consentono al client di verificare la firma senza possedere la chiave segreta.
Esempio di un header decodificato:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-1"
}
Payload contiene claims — dichiarazioni sul soggetto. I claims sono divisi in tre tipi: registrati (iss, sub, aud, exp, nbf, iat, jti), pubblici (definiti dallo sviluppatore nel Registro IANA) e privati (concordati tra le parti). sub (subject) è l'identificatore univoco dell'utente. exp (expiration) è il timestamp di scadenza del token. iss (issuer) è l'emittente del token.
{
"sub": "user-abc-123",
"iss": "https://auth.example.com",
"aud": "my-mobile-app",
"exp": 1812345678,
"iat": 1812342078,
"role": "premium_user"
}
Signature viene creata applicando l'algoritmo di firma alla concatenazione di header e payload utilizzando una chiave segreta o privata. La formula: HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) per HMAC, o RSASHA256(...) per algoritmo asimmetrico. Il destinatario calcola la firma allo stesso modo e la confronta con quella ricevuta — se corrispondono, i dati non sono stati alterati.
Il flusso di lavoro con JWT si compone di due fasi: creazione (emissione) del token da parte del server di autenticazione e verifica del token da parte del client o server di risorse. Il server di autenticazione riceve le credenziali dell'utente, crea un payload con claims e lo firma. Il JWT risultante viene inviato al client in risposta a una richiesta di login o nel corpo della risposta OAuth 2.0 / OpenID Connect.
Nelle applicazioni mobili, JWT viene utilizzato come segue: dopo un login riuscito, l'utente riceve un token di accesso in formato JWT. L'applicazione lo memorizza in un archivio sicuro (Keychain su iOS, EncryptedSharedPreferences su Android). Ad ogni richiesta API, l'applicazione aggiunge l'intestazione Authorization: Bearer <token>. Il server API verifica la firma JWT, estrae i claims e prende decisioni di accesso basate su di essi — senza interrogare il database.
Secondo Google Codelabs, 2025, l'uso di JWT in Firebase Authentication riduce il numero di richieste al server di autenticazione del 40–60% rispetto ai token di sessione, poiché i dati vengono verificati localmente su ogni microservizio. Ciò è particolarmente importante per architetture ad alto carico, dove ogni millisecondo di latenza influisce sull'esperienza utente. Con 50.000 richieste al minuto, il passaggio a JWT può far risparmiare fino a 10 istanze server che gestiscono richieste di introspezione.
JWT e Session Token risolvono lo stesso problema — autenticazione delle richieste — ma differiscono fondamentalmente nell'architettura. Session Token è una stringa identificativa casuale che fa riferimento ai dati di sessione memorizzati sul server (stateful). JWT è un token autocontenuto che contiene tutti i dati al suo interno (stateless).
| Parametro | JWT | Session Token |
|---|---|---|
| Archiviazione dati | All'interno del token (autocontenuto) | Sul server (archivio sessioni) |
| Scalabilità | Non richiede archiviazione condivisa | Richiede Redis/DB per multi-server |
| Revoca del token | Complessa (necessita blacklist) | Semplice (eliminare sessione dal DB) |
| Dimensione | Grande (500–2000 byte) | Piccola (16–64 byte) |
| Verifica firma | Crittografica | Nessuna (confronto stringhe) |
JWT vince nei sistemi distribuiti: i microservizi possono verificare il token localmente senza un archivio condiviso. Ad esempio, in un'architettura con cinque microservizi, ogni servizio verifica JWT in 1–2 ms senza chiamata di rete, mentre un session token richiede una query Redis centralizzata ad ogni richiesta, aggiungendo 10–30 ms di latenza. Tuttavia, JWT è difficile da revocare — una volta emesso, è valido fino alla scadenza. Session Token è facile da revocare eliminando il record dal DB o da Redis.
Per le applicazioni mobili, un approccio combinato — JWT con breve durata (15–30 minuti) e Refresh Token — offre un equilibrio tra prestazioni e sicurezza. JWT viene utilizzato per l'accesso alle API, mentre il refresh token (solitamente opaco) viene utilizzato per ottenere nuovi JWT. Se un JWT viene compromesso, l'attaccante ha accesso per 15–30 minuti; se un refresh token viene compromesso, la sessione viene bloccata tramite rotazione e rilevamento del riutilizzo.
La sicurezza di JWT dipende da una corretta implementazione. La vulnerabilità più comune è l'attacco “alg none”: un attaccante modifica l'header del token in “alg”: “none”, e il server, senza verificare l'algoritmo, accetta il token falsificato. Protezione: verificare sempre che l'algoritmo nell'header corrisponda a quello previsto (RS256, ES256) e rifiutare i token con alg: none.
Le vulnerabilità di JWT includono anche: chiave segreta debole per HMAC (decifrata in minuti), perdita della chiave privata (firmare qualsiasi dati per conto del server), memorizzazione di dati sensibili nel payload (JWT non crittografa, firma soltanto), attacco di iniezione JWK header (iniezione di una chiave pubblica personalizzata). L'uso di librerie affidabili — Nimbus JOSE + JWT, jjwt (io.jsonwebtoken), PyJWT — riduce il rischio di sfruttamento di queste vulnerabilità.
Una misura di sicurezza aggiuntiva è JWK Thumbprint (RFC 7638): associazione di una chiave pubblica al token tramite un'impronta (thumbprint) nell'header. Se il server memorizza l'impronta prevista per ogni client, l'iniezione JWK header diventa impossibile — il server rifiuta qualsiasi chiave che non corrisponda a quella registrata. L'OAuth Security Workshop 2025 raccomanda JWK Thumbprint come protezione obbligatoria per tutti i JWT utilizzati in applicazioni finanziarie e mediche.
La libreria jjwt (auth0/java-jwt) consente di creare e verificare JWT in un'applicazione Android in poche righe. Nell'esempio seguente, il server genera un token con sub e role, e il client verifica la firma. Per la memorizzazione sicura della chiave segreta sul server, utilizzare variabili d'ambiente o un HSM (Hardware Security Module) — la memorizzazione della chiave nel codice o in un file di configurazione è un grave errore di sicurezza.
val secret = "my-256-bit-secret-key-here"
val token = JWT.create()
.withSubject("user-abc-123")
.withIssuer("auth.example.com")
.withClaim("role", "premium_user")
.withExpiresAt(Date(System.currentTimeMillis() + 3600000))
.sign(Algorithm.HMAC256(secret))
// Invio del token al client
println("JWT: $token")
fun verifyToken(token: String): Boolean {
return try {
val decoded = JWT.require(Algorithm.HMAC256(secret))
.withIssuer("auth.example.com")
.build()
.verify(token)
// Firma valida, claims estratti
println("Subject: ${decoded.subject}")
true
} catch (e: Exception) {
println("Token non valido: ${e.message}")
false
}
}
Domande Frequenti
No. JWT viene firmato, non crittografato — chiunque può decodificare il payload Base64 e leggere i dati. Le informazioni sensibili (password, numeri di carta, dati personali) devono essere trasmesse solo in forma crittografata tramite JWE (JSON Web Encryption).
Si raccomanda ES256 (ECDSA con P-256) — offre un livello di sicurezza equivalente a RSA 2048-bit con una dimensione della firma significativamente inferiore. RS256 è adatto per la compatibilità con sistemi legacy. HS256 (HMAC) richiede uno scambio sicuro della chiave segreta, che è più complesso in un'architettura distribuita.
JWT non può essere revocato direttamente — è valido fino a exp. Soluzioni: utilizzare una durata breve (15–30 minuti), mantenere una blacklist di jti (JWT ID) revocati sul server, o associare i token a una versione della chiave segreta. Il refresh token viene revocato in modo standard — rimuovendolo dall'archivio.
Bearer token è un concetto: qualsiasi token che il possessore può utilizzare per l'accesso. JWT è un formato di token specifico. Un Bearer token può essere un JWT o una stringa opaca. JWT aggiunge l'autocontenimento e la verifica crittografica al concetto Bearer.
Un JWT tipico con firma RS256 occupa 500–2000 byte. Se il payload contiene molti claims personalizzati o viene utilizzata una firma asimmetrica con chiave grande, la dimensione può raggiungere 4–5 KB. Questo è significativamente più grande di un session token (16–64 byte), influenzando la dimensione delle intestazioni HTTP.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche