Un JSON Web Token è un token compatto e sicuro per gli URL che trasporta claim JSON firmati e permette di verificare le richieste senza sessioni memorizzate. È l’inquadramento della RFC 7519 stessa – “un mezzo compatto e sicuro per gli URL di rappresentare claim da trasferire fra due parti” – e le due idee che contiene reggono tutto quello che segue: il token contiene i propri fatti, e una firma rende quei fatti controllabili da chiunque abbia la chiave giusta, senza alcun database nel giro.
Punti chiave
| Domanda | Risposta |
|---|---|
| La forma | header.payload.signature – tre parti Base64Url unite da punti |
| Il trucco | Chiunque può decodificarlo; solo chi ha la chiave può falsificarlo |
| Non è crittografia | Il payload è leggibile – firmato ≠ segreto (è il compito di JWE) |
| Il baratto | Verifica stateless ↔ nessuna revoca integrata prima di exp |
| La disciplina | Fissare gli algoritmi · validare iss/aud/exp · vite brevi + rotazione dei refresh token |
Un JWT, decodificato
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJzdWIiOiJ1c3ItOGZrMiIsInJvbGUi… . dBjftJeZ4CVP…
header { "alg": "HS256", "typ": "JWT" }
payload { "sub": "usr-8fk2", "role": "editor",
"iss": "https://api.example.com", "aud": "example-web",
"iat": 1767024900, "exp": 1767025800 } ← durata: 15 minuti
signature HMACSHA256( base64url(header) + "." + base64url(payload), secret )
Chiunque può DECODIFICARE le prime due parti – Base64Url è imballaggio, non crittografia.
Solo chi ha la chiave può FALSIFICARE la terza – ed è in questo che sta tutto il trucco.
Il contrasto che vale la pena vedere in codice – l’alternativa con stato su cui si misura ogni decisione sui JWT:
// JavaScript / Node.js — Back4app JS SDK
// The stateful contrast: Back4app issues revocable session tokens
const user = await Parse.User.logIn('ada', 'correct-horse-battery');
const token = user.getSessionToken(); // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await Parse.User.logOut(); // token invalid NOW — no waiting for an exp claim // Flutter / Dart — Back4app Flutter SDK
// The stateful contrast: Back4app issues revocable session tokens
final user = ParseUser('ada', 'correct-horse-battery', null);
await user.login();
final token = user.sessionToken; // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await user.logout(); // token invalid NOW — no waiting for an exp claim // iOS / Swift — Back4app Swift SDK
// The stateful contrast: Back4app issues revocable session tokens
let user = try await User.login(username: "ada", password: "correct-horse-battery")
let token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
try await User.logout() // token invalid NOW — no waiting for an exp claim // Android / Kotlin — Back4app Android SDK
// The stateful contrast: Back4app issues revocable session tokens
val user = ParseUser.logIn("ada", "correct-horse-battery")
val token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
ParseUser.logOut() // token invalid NOW — no waiting for an exp claim Come funziona la verifica
La precisione che le spiegazioni saltano: “JWT” nomina il formato dei claim; ciò che tutti fanno realmente circolare è un JWS (RFC 7515) – la serializzazione firmata – mentre JWE è il fratello cifrato, per i payload che devono restare illeggibili. E la verifica sono due lavori, non uno: controllare la firma e poi validare i claim – exp e nbf contro l’orologio, iss contro la propria lista di emittenti consentiti, aud contro l’identificatore di questo servizio. Un token firmato alla perfezione ma scaduto, proveniente dall’emittente sbagliato o coniato per un’altra audience è un attacco firmato alla perfezione.
I claim: il vocabolario del payload
| Claim | Nome | Compito di chi verifica |
|---|---|---|
iss | Emittente (issuer) | È un emittente di cui fidarsi? |
sub | Soggetto (subject) | Di chi si parla – l’ID utente stabile |
aud | Audience | È stato coniato per me? Rifiutare i token altrui |
exp | Scadenza (expiration) | Rifiutare dopo questo timestamp Unix |
iat / nbf | Emesso il / non prima di | Verificare la coerenza della finestra di validità |
jti | ID del token | Identificatore univoco – l’aggancio di cui una denylist ha bisogno |
| personalizzati | Ruoli, tenant, piano… | Semantica dell’app – minima, mai segreta |
Conviene tenere i payload leggeri per due motivi: i token viaggiano a ogni richiesta negli header (i cookie si fermano intorno ai 4 KB, e ogni claim è banda ripetuta) e tutto ciò che contengono è leggibile da chiunque detenga il token.
JWT vs. token di sessione
| JWT (stateless) | Token di sessione (con stato) | |
|---|---|---|
| Il token è | Lo stato stesso, firmato | Un puntatore opaco allo stato del server |
| Costo per richiesta | Controllo della firma, nessuna lettura | Una lettura nell’archivio delle sessioni |
| Revoca | Nessuna fino a exp – per progetto | Istantanea – si elimina la sessione |
| Auth fra servizi | Verifica qualsiasi servizio che abbia la chiave | I servizi devono condividere l’archivio delle sessioni |
| Disconnettersi significa | Il client lo scarta; il token resta valido | Il token muore davvero |
| Casa ideale | API, microservizi, verifica da parte di terzi | App con un solo backend, sessioni sensibili |
La verità fuori moda con cui diverse guide di buone pratiche ormai aprono: per un’app renderizzata lato server con un solo backend, le sessioni del framework sono più semplici e più controllabili – i JWT si guadagnano il posto quando i token devono essere verificati fra servizi o da parti che non possono interpellare l’emittente a ogni richiesta.
Il problema della revoca, onestamente
L’assenza di stato è l’impossibilità di revocare – la stessa proprietà, descritta due volte. Un token firmato è valido fino a exp qualunque cosa sia successa nel frattempo: disconnessione, cambio di password, blocco dell’account. Ogni rimedio reintroduce stato, quindi resta solo da scegliere il gusto: una denylist indicizzata su jti + iss (la costruzione raccomandata da OWASP) consultata a ogni richiesta – poco stato, ma pur sempre stato; il versionamento delle chiavi, che revoca tutti in un colpo solo; oppure l’architettura standard – access token a vita breve (5–15 minuti) più refresh token revocabili con rotazione: ogni rinnovo emette un nuovo refresh token e ritira il vecchio, così che uno rubato muoia al primo riutilizzo, e il riuso di un token ritirato segnala il furto e uccide l’intera famiglia di token. La latenza di revoca diventa allora pari alla vita dell’access token – ed è la vera ragione per cui quelle vite sono brevi.
Scegliere un algoritmo – e gli attacchi a quella scelta
| HS256 (HMAC) | RS256 (RSA) | Ed25519 / ES256 | |
|---|---|---|---|
| Chiavi | Un solo segreto condiviso | La privata firma, la pubblica verifica | La privata firma, la pubblica verifica |
| A chi verifica serve | Il segreto (con cui si può anche falsificare!) | Solo la chiave pubblica | Solo la chiave pubblica |
| Adatto a | Emittente = verificatore, una sola parte | Multi-servizio, terze parti | Idem, firme più piccole e veloci |
| Spigolo vivo | I segreti corti si rompono per forza bruta offline a partire da un solo token catturato – usare ≥ 256 bit casuali | Token più grandi, più lento | Supporto delle librerie più recente |
La sezione sugli attacchi per cui esiste la RFC 8725, in tre frasi. alg: "none" è un valore di header legale – le librerie che lo onorano accettano token non firmati. L’attacco per confusione: un verificatore che lascia scegliere l’algoritmo all’header del token può ricevere un token “HS256” firmato con la chiave RSA pubblica del server usata come segreto HMAC – un valore pubblico impiegato come valore privato. Entrambi muoiono allo stesso modo: chi verifica fissa nella configurazione gli algoritmi e le chiavi che accetta e non si fida mai dell’header per la scelta.
Dove conservare i JWT nel browser
| Posizione | L’XSS lo ruba? | Il CSRF lo invia? | Sopravvive al ricaricamento? | Verdetto |
|---|---|---|---|---|
| localStorage | Sì | No | Sì | Da evitare – leggibile dagli script |
| Cookie semplice | Sì (leggibile dagli script) | Sì | Sì | Il peggio di entrambi |
| Cookie HttpOnly + Secure + SameSite | No | Mitigato da SameSite | Sì | Bene – per il refresh token |
| In memoria | Solo finché dura l’iniezione | No | No | Bene – per l’access token |
| I token restano nel backend-for-frontend | Il browser non li ha mai | Valgono le regole dei cookie | Sì | Il più solido per le SPA |
Conta il modello di minaccia, non il folklore: localStorage baratta l’immunità al CSRF con il furto via XSS, i cookie barattano l’inverso – ed è per questo che il consenso divide la coppia: access token in memoria, refresh token in un cookie irrobustito.
Casi d’uso tipici
- Autenticazione di API – il bearer token dietro gli header
Authorizationdelle API REST e GraphQL. - Identità nei microservizi – un solo token emesso dalla gateway e verificato in modo indipendente da ogni servizio, senza archivio di sessioni condiviso.
- ID token di OpenID Connect – l’asserzione di identità firmata di ogni login social – sempre un JWT.
- Passaggi di consegne fra sistemi – link firmati, payload di webhook, autorizzazioni al download: claim verificati da una parte che non può richiamare l’emittente.
- Indizi di autorizzazione stateless – ruoli e ID di tenant trasportati nei claim, con il controllo che fa fede sempre lato server.
JWT o sessioni? Matrice decisionale
| Situazione | Scegliere |
|---|---|
| Un solo backend, app renderizzata lato server | Le sessioni – più semplici, revocabili all’istante |
| API pubblica consumata da molti servizi | I JWT, con chiavi asimmetriche |
| Microservizi dietro una gateway | I JWT – verifica locale, nessun archivio condiviso |
| Il blocco immediato è un requisito inderogabile | Le sessioni, oppure JWT + denylist ed exp breve |
| Terze parti devono verificare le asserzioni emesse | I JWT – è il terreno di casa del formato |
| App mobile davanti a un BaaS | Il meccanismo di sessione della piattaforma – ha già scelto |
Limiti e trade-off
- L’irrevocabilità è strutturale. Ogni mitigazione – denylist, vite brevi, rotazione – è un rientro parziale dello stato che era stato tolto; va messa nel conto prima di scegliere il modello stateless.
- I claim invecchiano. I ruoli sono un’istantanea scattata all’emissione; un amministratore retrocesso resta amministratore fino alla scadenza. Le vite brevi limitano la finestra di obsolescenza.
- Il payload è pubblico. Va trattato come leggibile dall’utente e da qualsiasi ladro di token: identificatori sì, segreti e dati personali no.
- I valori predefiniti delle librerie hanno fatto vittime. Fissare gli algoritmi, validare i claim e controllare
typsono responsabilità della propria configurazione, non garanzie della libreria. - La dimensione si accumula. Ogni claim viaggia a ogni richiesta; payload generosi tassano la banda mobile e possono sforare i limiti dei cookie.
I token su Back4app
Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. La scelta della piattaforma stessa illustra la matrice decisionale: le sessioni dei client usano token di sessione revocabili, lato server – le schede di codice qui sopra – così che disconnessione, cambi di password e chiusura della sessione dalla dashboard abbiano effetto immediato, senza finestre di scadenza da attendere. I JWT compaiono dove è giusto che siano: gli ID token OIDC dei provider di identità vengono verificati lato server dagli adattatori di autenticazione durante il login social, e le funzioni Cloud Code possono coniare o verificare JWT per i passaggi di consegne con terze parti usando librerie standard – con il segreto di firma nella configurazione lato server, mai spedito ai client. Con stato dove conta il controllo, stateless ai bordi dove la verifica deve viaggiare: l’architettura che questo articolo difende, già assemblata.
Domande frequenti
Che cos'è un JWT in parole semplici?
Una "carta d'identità" JSON firmata e codificata in Base64 che il server emette all'accesso. Il client la presenta a ogni richiesta e il server verifica la firma invece di cercare una sessione – il token stesso porta con sé chi è l'utente e fino a quando. Secondo la RFC che lo definisce, la pronuncia suggerita è quella della parola inglese "jot".
Quali sono le tre parti di un JWT?
Header (tipo di token e algoritmo di firma), payload (i claim – i dati JSON veri e propri) e firma, ciascuno codificato in Base64Url e uniti da punti in header.payload.signature. La firma è calcolata sulle prime due parti, quindi qualsiasi manomissione di quelle rompe la verifica.
Un JWT è cifrato?
No – codificato, non cifrato. Base64Url è un formato di trasporto che chiunque può invertire; basta incollare un JWT qualsiasi in un decodificatore e il payload si legge in chiaro. La firma impedisce la manomissione, non la lettura. Nel payload di un JWT non vanno mai messi segreti né dati personali sensibili; per le esigenze reali di riservatezza esiste la variante cifrata (JWE).
Qual è la differenza tra un JWT e un token di sessione?
Dove risiede lo stato. Un token di sessione è un ID opaco che punta a uno stato lato server – una lettura per richiesta, revocabile all'istante. Un JWT porta lo stato dentro di sé – nessuna lettura, verificabile da qualsiasi servizio che abbia la chiave, ma valido fino alla scadenza qualunque cosa accada. Scalabilità stateless contro controllo immediato.
Dove conviene conservare un JWT nel browser?
Non in localStorage – qualsiasi script iniettato può leggerlo, il che trasforma un XSS in un furto di token. Il consenso attuale: access token in memoria, refresh token in un cookie HttpOnly, Secure e SameSite, e vale la pena considerare il pattern backend-for-frontend, che tiene i token completamente fuori dal browser.
Un JWT può essere revocato?
Non per progetto – un token firmato è valido fino al suo claim exp, ed è il prezzo dell'assenza di stato. Tutti gli aggiramenti reintroducono stato: una denylist indicizzata sul claim jti, chiavi versionate oppure – la risposta standard – access token con vita molto breve abbinati a refresh token revocabili e a rotazione.
Qual è la differenza tra HS256 e RS256?
La simmetria. HS256 firma e verifica con un unico segreto condiviso – accettabile solo quando emittente e verificatore sono la stessa parte, e solo con un segreto lungo e casuale. RS256 (e il più recente Ed25519) firma con una chiave privata mentre chiunque verifica con quella pubblica – la scelta predefinita non appena più di un servizio controlla i token.
Qual è la differenza tra JWT e OAuth?
Categorie diverse: JWT è un formato di token; OAuth 2.0 è un framework di autorizzazione. Si compongono – i flussi OAuth emettono comunemente access token che sono anche JWT, e gli ID token di OpenID Connect lo sono sempre. Uno dice come è costruito un token; l'altro, come i token vengono distribuiti.