← Tutti gli articoli

Come decodificare un JWT e leggere le sue claim

Un JWT è composto da tre sezioni Base64url. Impara a decodificare header e payload in JavaScript e Python — e perché decodificare un token non è lo stesso che verificarlo.

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_wW1gFWFOEjXk

I 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 ispezione

La 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:

  1. Whitelist di algoritmi —— rifiuta tutto ciò che è fuori dal tuo set atteso (es. ['RS256']). Non accettare mai none.
  2. Firma —— verifica con la chiave giusta (cercala tramite kid nel tuo JWKS).
  3. exp —— il token non deve essere scaduto.
  4. nbf (not-before) —— non deve essere usato troppo presto.
  5. iat —— sanity check che l'emissione non sia assurdamente nel futuro (tollerando il drift di orologio).
  6. iss —— l'issuer deve corrispondere all'autorità fidata attesa.
  7. 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

ClaimSignificato
issIssuer —— chi ha creato il token
subSubject —— di chi parla il token (spesso un ID utente)
audAudience —— a chi è destinato il token
expExpiration —— timestamp Unix dopo il quale il token non è valido
iatIssued-at —— timestamp Unix di creazione
nbfNot-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. Tratta kid come 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“. Rifiuta alg: none esplicitamente 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