Un JWT (JSON Web Token) sono tre sezioni codificate in Base64url unite da punti. Decodificarne uno per leggerne i claim richiede una singola chiamata di funzione e nessuna chiave segreta —— proprio per questo il payload di un JWT non è un posto sicuro per dati sensibili. Questa guida spiega cos'è un JWT, come decodificare ogni parte e la differenza critica tra decodificare e verificare un token.
Cos'è un JWT?
Un JWT è un token compatto e URL-safe usato per trasportare claim —— tipicamente identità e permessi di un utente —— tra parti. È standardizzato come RFC 7519. Un token ha questo aspetto:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSJ9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkI due punti lo dividono in tre parti: header, payload e signature.
Le tre parti
- Header —— un piccolo oggetto JSON che descrive l'algoritmo di firma, es.
{"alg":"HS256","typ":"JWT"}. - Payload —— un oggetto JSON con claim come
sub(subject),exp(scadenza) e qualsiasi campo personalizzato. - Signature —— una firma crittografica su header e payload, usata per rilevare manomissioni. Non è leggibile dagli umani e non è JSON.
Header e payload sono JSON codificato in Base64url. La firma è prodotta con JWS (RFC 7515).
Come decodificare un JWT in JavaScript
Spezza sui punti e decodifica Base64url le prime due parti. Nota che JWT usa Base64url (con - e _ al posto di + e /, senza padding), quindi converti prima di decodificare:
function decodeJwt(token) {
const [headerB64, payloadB64] = token.split('.');
const decode = (b64url) => {
const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
const json = atob(b64);
return JSON.parse(json);
};
return { header: decode(headerB64), payload: decode(payloadB64) };
} Per piena sicurezza Unicode, decodifica il Base64 in byte e passali per TextDecoder invece di usare il solo atob.
Come decodificare un JWT in Python
import base64, json
def decode_jwt(token):
header_b64, payload_b64, _sig = token.split('.')
def decode(part):
padded = part + '=' * (-len(part) % 4)
return json.loads(base64.urlsafe_b64decode(padded))
return decode(header_b64), decode(payload_b64)Decodificare non è verificare
Questo è il punto più importante sui JWT. Chiunque può decodificare un token e leggerne il payload —— niente chiave richiesta. Decodificare ti dice cosa il token afferma; non ti dice se quei claim sono affidabili.
Per fidarti di un token devi verificare la firma contro la chiave segreta o pubblica del server, usando una libreria revisionata —— non implementare mai da zero:
import jwt from 'jsonwebtoken';
// Verifica firma E controlla scadenza; lancia se non valido
const claims = jwt.verify(token, process.env.JWT_SECRET);
// jwt.decode(token) —— NON verifica; solo per ispezioneLa verifica lato server è ciò che rende un JWT una credenziale. Trattare un payload decodificato come affidabile senza verificare la firma è un classico bug di autenticazione.
Il payload di un JWT non è cifrato
Dato che il payload è solo JSON codificato in Base64url, offre zero riservatezza. È lo stesso fraintendimento trattato in Base64 non è cifratura: la codifica può essere invertita da chiunque.
Non mettere mai password, numeri completi di carta o dati personali sensibili in un payload JWT. Se davvero ti serve un token cifrato, usa JWE (JSON Web Encryption, RFC 7516) —— uno standard separato e molto meno comune. Ed evita di incollare token reali in decoder online non affidabili; vedi perché non dovresti incollare JSON sensibile in strumenti online.
Ordine di validazione: prima firma, poi claim
Quando verifichi un token in produzione, l'ordine conta. Una checklist sicura:
- Whitelist di algoritmi —— rifiuta tutto ciò che è fuori dal tuo set atteso (es.
['RS256']). Non accettare mainone. - Firma —— verifica con la chiave giusta (cercala tramite
kidnel tuo JWKS). exp—— il token non deve essere scaduto.nbf(not-before) —— non deve essere usato troppo presto.iat—— sanity check che l'emissione non sia assurdamente nel futuro (tollerando il drift di orologio).iss—— l'issuer deve corrispondere all'autorità fidata attesa.aud—— l'audience deve includere il tuo servizio.
La maggior parte delle librerie JWT fa 1–4 di default e richiede opt-in per 6–7. Finché iss e aud non sono controllati, un token valido per un altro servizio del tuo ecosistema potrebbe essere riusato contro il tuo.
Claim standard comuni
| Claim | Significato |
|---|---|
iss | Issuer —— chi ha creato il token |
sub | Subject —— di chi parla il token (spesso un ID utente) |
aud | Audience —— a chi è destinato il token |
exp | Expiration —— timestamp Unix dopo il quale il token non è valido |
iat | Issued-at —— timestamp Unix di creazione |
nbf | Not-before —— il token non è valido fino a questo momento |
L'header kid e la trappola alg: none
Due campi di header compaiono abbastanza spesso nei JWT reali da meritare di essere riconosciuti a colpo d'occhio:
kid(key ID) —— un suggerimento che dice al verificatore quale chiave di un set (JWKS) ha firmato questo token. Vive nell'header così il verificatore può scegliere la chiave giusta prima di controllare la firma. Trattakidcome input non affidabile: cercalo in una whitelist fissa (o in un JWKS scaricato), non usarlo mai come percorso di file, parametro SQL o URL.alg: "none"—— un algoritmo legale ma storicamente pericoloso che significa „senza firma“. I decoder lo decodificano volentieri e un verificatore permissivo può accettarlo, trasformando qualsiasi payload contraffatto in token „valido“. Rifiutaalg: noneesplicitamente e configura la tua libreria con l'algoritmo/i esatto/i atteso/i (es.['RS256']) invece di fidarti di quanto dichiarato dall'header del token stesso.
Decodifica un JWT nel tuo browser
Per ispezionare velocemente i claim di un token, incolla una delle sezioni Base64url nel decoder Base64 di fixjson —— decodifica al JSON sottostante in locale, senza inviare nulla a un server. Per token non sensibili puoi usare anche un debugger dedicato, ma per qualsiasi cosa con dati utente reali, uno strumento local-first è la scelta sicura.
Domande frequenti
Come decodifico un JWT?
Spezza il token sui suoi punti e decodifica Base64url le prime due parti (header e payload) in JSON. La terza è la firma e non è leggibile dagli umani. Per decodificare non serve alcuna chiave.
Decodificare un JWT è lo stesso che verificarlo?
No. Decodificare legge solo i claim; chiunque può farlo. Verificare controlla la firma contro una chiave segreta o pubblica per confermare che il token sia autentico e non manomesso. Verifica sempre sul server prima di fidarti di un token.
Chiunque può leggere i dati di un JWT?
Sì. Il payload è codificato in Base64url, non cifrato —— è testo in chiaro per chiunque abbia il token. Non mettere mai segreti in un payload JWT. Vedi Base64 non è cifratura.
Qual è la differenza tra JWT, JWS e JWE?
Un JWT firmato usa JWS (RFC 7515) —— payload leggibile, evidenza di manomissione. JWE (RFC 7516) cifra il payload per la riservatezza. I JWT comuni che vedi tutti i giorni sono JWS.
A cosa serve il claim kid?
kid è un campo di header (non un claim del payload) che identifica quale chiave del JWKS ha firmato il token, così il verificatore sceglie quella giusta. Cercalo contro un set fisso di chiavi che controlli tu —— non fidarti come percorso o URL.
Perché alg: "none" è pericoloso?
Significa „senza firma“. Un verificatore permissivo che rispetta l'algoritmo dichiarato dal token stesso accetterà qualsiasi payload contraffatto. Configura sempre la tua libreria con l'algoritmo esatto atteso (es. ['RS256']) e rifiuta esplicitamente none.
Ispeziona token in locale
- Encoder e decoder Base64 —— decodifica le sezioni header/payload di un JWT nel browser
- Base64 non è cifratura —— perché chiunque può leggere un payload JWT
- Perché non incollare JSON sensibile online —— maneggiare token reali in sicurezza
- RFC 7519: JSON Web Token —— lo standard in sé