I JSON Web Token (JWT) sono molto usati per l’autenticazione delle API e per il single sign-on. Il formato è semplice, ma lascia molte decisioni di sicurezza allo sviluppatore e alla libreria. La RFC 8725, JWT Best Current Practices, raccoglie gli errori noti e spiega come evitarli; questo articolo esamina quelli che controlleremmo per primi.
Un JWT è firmato, non segreto
Un JWT firmato, definito nella RFC 7519, ha tre parti separate da punti: un header, un payload di claim e una firma. Le prime due sono solo codificate in base64url. Chiunque possieda il token può leggerle. Ecco un esempio decodificato:
{
"alg": "RS256",
"typ": "at+jwt",
"kid": "2026-10-key-1"
}
{
"iss": "https://auth.example.eu",
"sub": "user-4821",
"aud": "api.example.eu",
"iat": 1792140000,
"exp": 1792140900,
"role": "editor"
}
La firma dimostra che i claim non sono stati modificati. Non li nasconde. La RFC 7519 stabilisce che, quando un token contiene informazioni rilevanti per la privacy, devono essere adottate misure per impedirne la divulgazione, ad esempio usando un JWT cifrato (JWE). In pratica, tenga fuori dal payload i dati personali, gli identificativi interni che non pubblicherebbe e qualsiasi segreto.
Errore 1: accettare "alg": "none"
La RFC 7519 consente JWT “non protetti” con "alg": "none" e una firma vuota. La RFC 8725 descrive librerie che accettavano questi token e saltavano del tutto la verifica della firma. Un attaccante poteva quindi scrivere tutti i claim che voleva.
La RFC 7518 stabilisce che le implementazioni non devono accettare token non protetti per impostazione predefinita, e il REST Security Cheat Sheet di OWASP dice di non consentirli affatto. Verifichi che la sua libreria rifiuti none e lo provi con un token costruito appositamente.
Errore 2: lasciare che sia il token a scegliere l’algoritmo
Il valore alg dell’header è scelto da chi ha creato il token, che potrebbe essere un attaccante. La RFC 8725 descrive il classico attacco di confusione: un server si aspetta RS256, verificato con una chiave pubblica RSA. L’attaccante cambia alg in HS256 e firma il token con HMAC, usando quella chiave pubblica come segreto. Una libreria che segue l’header lo verifica con successo, e la chiave pubblica è, per definizione, pubblica.
La soluzione è decidere l’algoritmo sul server. OWASP afferma che la verifica non deve basarsi sull’header del JWT per selezionare l’algoritmo. La RFC 8725 aggiunge che ogni chiave dovrebbe essere usata con un solo algoritmo. Nel codice, il risultato è qualcosa di simile a:
verify(token,
key = issuer_public_key,
algorithms = ["RS256"],
issuer = "https://auth.example.eu",
audience = "api.example.eu")
Errore 3: segreti HMAC deboli
Con HS256, chiunque intercetti un token può tentare di indovinare il segreto offline, alla velocità consentita dal proprio hardware. La RFC 8725 stabilisce che password memorizzabili da una persona non devono essere usate direttamente come chiavi HMAC. La RFC 7518 richiede una chiave lunga almeno quanto l’output dell’hash, quindi 256 bit per HS256.
openssl rand -base64 32
Il comando produce 32 byte casuali, cioè 256 bit. Li conservi in un secrets manager, non nel repository.
I segreti condivisi hanno un secondo punto debole. Ogni servizio in grado di verificare un token HMAC può anche crearne uno. Per questo OWASP preferisce le firme ai MAC: se più servizi convalidano i token, un solo servizio compromesso potrebbe altrimenti generare token per tutti. Gli algoritmi asimmetrici come RS256 o ES256 permettono di condividere solo la chiave pubblica.
Errore 4: saltare la convalida dei claim
Una firma valida dimostra solo chi ha emesso il token. Sono i claim a stabilire se questo servizio debba accettarlo ora.
exp- la RFC 7519 stabilisce che il token non deve essere accettato a partire da questo momento. È ammesso un piccolo margine per lo scostamento degli orologi, di solito non più di pochi minuti.nbf- se presente, rifiuti il token prima di questo momento.iss- verifichi che l’emittente sia quello di cui si fida e che possieda la chiave di firma.aud- la RFC 7519 stabilisce che, se il servizio non si trova tra i destinatari, il token deve essere rifiutato.
OWASP raccomanda di richiedere e convalidare per impostazione predefinita iss, aud ed exp per gli access token delle API. La RFC 8725 spiega perché il destinatario è importante. Senza di esso, un token emesso per un servizio può essere riutilizzato presso un altro che si fida dello stesso emittente. Raccomanda inoltre una tipizzazione esplicita con l’header typ, in modo che un tipo di token non possa essere usato dove ne è previsto un altro. Per gli access token OAuth, la RFC 9068 usa at+jwt, come nell’esempio precedente.
Errore 5: fidarsi di kid, jku e x5u
Anche i parametri dell’header che indicano al server dove trovare la chiave sono controllati dall’attaccante. La RFC 8725 elenca i rischi:
kid(ID della chiave), se usato in una ricerca su database o directory, può consentire SQL injection o LDAP injection. Lo stesso vale se viene usato per costruire un percorso di file.jkuex5upuntano a un URL da cui recuperare le chiavi. Seguire qualsiasi URL espone a server-side request forgery, e un attaccante potrebbe farlo puntare alla propria chiave.
Tratti kid come un’etichetta opaca e lo cerchi in un elenco fisso di chiavi sotto il suo controllo. Recuperi le chiavi solo da un elenco di URL consentiti; la RFC 8725 aggiunge che con queste richieste non dovrebbero essere inviati cookie.
Errore 6: token a lunga durata e nessuna revoca
Un JWT resta valido fino alla scadenza, anche dopo che l’utente ha effettuato il logout o un amministratore ha disattivato l’account. OWASP osserva che i token possono sopravvivere alla sessione che rappresentano.
- Mantenga breve la durata degli access token, nell’ordine dei minuti anziché dei giorni.
- Usi refresh token che il server verifica a ogni utilizzo e può revocare.
- Per logout e sospensioni degli account, mantenga una denylist degli ID dei token (
jti) fino alla loro scadenza, come suggerisce OWASP. - Ruoti le chiavi di firma secondo un calendario e preveda un processo collaudato per una rotazione d’emergenza.
Errore 7: conservare i token dove gli script possono leggerli
I token conservati in localStorage possono essere letti da qualsiasi JavaScript presente nella pagina. L’HTML5 Security Cheat Sheet di OWASP avverte che una sola vulnerabilità di cross-site scripting può sottrarre tutto il contenuto di questi oggetti di archiviazione, e sconsiglia di conservarvi identificativi di sessione.
Per le applicazioni browser, un cookie HttpOnly e Secure con un’impostazione SameSite adeguata tiene il token lontano dagli script. I cookie introducono però il rischio di cross-site request forgery, quindi va pianificata anche questa difesa. Qualunque sia la scelta, una Content Security Policy rigorosa riduce in partenza la probabilità di XSS.
Checklist di convalida
- Fissi sul server gli algoritmi accettati e rifiuti
none. - Usi una libreria che verifica la firma prima di leggere qualsiasi claim.
- Convalidi
exp,nbf,isseauda ogni richiesta. - Usi
typo destinatari distinti per separare i tipi di token. - Cerchi le chiavi tramite
kidin un insieme fisso; non recuperi mai chiavi da URL arbitrari. - Usi chiavi HMAC casuali di almeno 256 bit, oppure chiavi asimmetriche.
- Tenga i payload liberi da segreti e da dati personali non necessari.
- Mantenga breve la durata degli access token e supporti la revoca.
- Conservi i token lato browser fuori dalla portata di JavaScript.
Da dove iniziare
Incolli un token del suo ambiente di test nel nostro decoder JWT per vedere esattamente cosa rivela, e confronti i suoi valori alg, aud ed exp con questo elenco. Eviti di incollare token di produzione attivi in qualsiasi strumento online.
Se desidera un secondo parere sulla progettazione dell’autenticazione o una revisione del codice che gestisce i token, il nostro team di sviluppo di software sicuro può aiutarla.
