Guida completa alla sicurezza dei JWT (JSON Web Token): best practice su validazione, archiviazione, algoritmi di firma e mitigazione delle vulnerabilità per applicazioni globali.
Token JWT: Best Practice di Sicurezza per Applicazioni Globali
I JSON Web Token (JWT) sono diventati un metodo standard per rappresentare in modo sicuro le attestazioni (claim) tra due parti. La loro struttura compatta, la facilità d'uso e l'ampio supporto su varie piattaforme li hanno resi una scelta popolare per l'autenticazione e l'autorizzazione in applicazioni web moderne, API e microservizi. Tuttavia, la loro adozione diffusa ha anche portato a un maggiore controllo e alla scoperta di numerose vulnerabilità di sicurezza. Questa guida completa esplora le best practice di sicurezza dei JWT per garantire che le vostre applicazioni globali rimangano sicure e resilienti contro potenziali attacchi.
Cosa sono i JWT e Come Funzionano?
Un JWT è un token di sicurezza basato su JSON composto da tre parti:
- Header: Specifica il tipo di token (JWT) e l'algoritmo di firma utilizzato (es. HMAC SHA256 o RSA).
- Payload: Contiene le attestazioni (claim), che sono dichiarazioni su un'entità (tipicamente l'utente) e metadati aggiuntivi. Le attestazioni possono essere registrate (es. emittente, soggetto, tempo di scadenza), pubbliche (definite dall'applicazione) o private (attestazioni personalizzate).
- Signature: Creata combinando l'header codificato, il payload codificato, una chiave segreta (per algoritmi HMAC) o una chiave privata (per algoritmi RSA/ECDSA), l'algoritmo specificato e firmando il risultato.
Queste tre parti sono codificate in Base64 URL e concatenate con punti (.
) per formare la stringa JWT finale. Quando un utente si autentica, il server genera un JWT, che il client archivia (tipicamente nel local storage o in un cookie) e include nelle richieste successive. Il server quindi valida il JWT per autorizzare la richiesta.
Comprendere le Vulnerabilità Comuni dei JWT
Prima di immergersi nelle best practice, è fondamentale comprendere le vulnerabilità comuni associate ai JWT:
- Confusione di Algoritmo (Algorithm Confusion): Gli aggressori sfruttano la possibilità di cambiare il parametro
alg
dell'header da un algoritmo asimmetrico forte (come RSA) a uno simmetrico debole (come HMAC). Se il server utilizza la chiave pubblica come chiave segreta nell'algoritmo HMAC, gli aggressori possono falsificare i JWT. - Esposizione della Chiave Segreta: Se la chiave segreta utilizzata per firmare i JWT viene compromessa, gli aggressori possono generare JWT validi, impersonando qualsiasi utente. Ciò può accadere a causa di fughe di codice, archiviazione non sicura o vulnerabilità in altre parti dell'applicazione.
- Furto di Token (XSS/CSRF): Se i JWT sono archiviati in modo non sicuro, gli aggressori possono rubarli tramite attacchi di Cross-Site Scripting (XSS) o Cross-Site Request Forgery (CSRF).
- Attacchi di Replay: Gli aggressori possono riutilizzare JWT validi per ottenere accessi non autorizzati, specialmente se i token hanno una lunga durata e non sono state implementate contromisure specifiche.
- Attacchi Padding Oracle: Quando i JWT sono crittografati con determinati algoritmi e il padding non è gestito correttamente, gli aggressori possono potenzialmente decrittare il JWT e accedere al suo contenuto.
- Problemi di Sfasamento del Clock (Clock Skew): Nei sistemi distribuiti, lo sfasamento del clock tra diversi server può portare a fallimenti nella validazione dei JWT, in particolare con le attestazioni di scadenza.
Best Practice di Sicurezza per i JWT
Ecco le best practice di sicurezza complete per mitigare i rischi associati ai JWT:
1. Scegliere l'Algoritmo di Firma Corretto
La scelta dell'algoritmo di firma è critica. Ecco cosa considerare:
- Evitare
alg: none
: Non permettere mai che l'headeralg
sia impostato sunone
. Questo disabilita la verifica della firma, consentendo a chiunque di creare JWT validi. Molte librerie sono state aggiornate per prevenire questo problema, ma assicuratevi che le vostre siano aggiornate. - Preferire Algoritmi Asimmetrici (RSA/ECDSA): Usare algoritmi RSA (RS256, RS384, RS512) o ECDSA (ES256, ES384, ES512) quando possibile. Gli algoritmi asimmetrici utilizzano una chiave privata per la firma e una chiave pubblica per la verifica. Questo impedisce agli aggressori di falsificare i token anche se ottengono l'accesso alla chiave pubblica.
- Gestire in Modo Sicuro le Chiavi Private: Archiviare le chiavi private in modo sicuro, utilizzando moduli di sicurezza hardware (HSM) o sistemi di gestione delle chiavi sicuri. Non inserire mai le chiavi private nei repository del codice sorgente.
- Ruotare le Chiavi Regolarmente: Implementare una strategia di rotazione delle chiavi per cambiare regolarmente le chiavi di firma. Ciò minimizza l'impatto in caso di compromissione di una chiave. Considerate l'uso di JSON Web Key Sets (JWKS) per pubblicare le vostre chiavi pubbliche.
Esempio: Utilizzo di JWKS per la Rotazione delle Chiavi
Un endpoint JWKS fornisce un insieme di chiavi pubbliche che possono essere utilizzate per verificare i JWT. Il server può ruotare le chiavi e i client possono aggiornare automaticamente il loro set di chiavi recuperando l'endpoint JWKS.
/.well-known/jwks.json
:
{
"keys": [
{
"kty": "RSA",
"kid": "key1",
"alg": "RS256",
"n": "...",
"e": "AQAB"
},
{
"kty": "RSA",
"kid": "key2",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}
2. Validare Correttamente i JWT
Una corretta validazione è essenziale per prevenire attacchi:
- Verificare la Firma: Verificare sempre la firma del JWT usando la chiave e l'algoritmo corretti. Assicuratevi che la vostra libreria JWT sia configurata correttamente e aggiornata.
- Validare le Attestazioni (Claim): Validare le attestazioni essenziali come
exp
(tempo di scadenza),nbf
(non prima di),iss
(emittente) eaud
(destinatario). - Controllare l'Attestazione
exp
: Assicurarsi che il JWT non sia scaduto. Implementare una durata ragionevole del token per minimizzare la finestra di opportunità per gli aggressori. - Controllare l'Attestazione
nbf
: Assicurarsi che il JWT non venga utilizzato prima del suo tempo di inizio validità. Ciò previene attacchi di replay prima che il token sia destinato ad essere utilizzato. - Controllare l'Attestazione
iss
: Verificare che il JWT sia stato emesso da un emittente fidato. Ciò impedisce agli aggressori di utilizzare JWT emessi da parti non autorizzate. - Controllare l'Attestazione
aud
: Verificare che il JWT sia destinato alla vostra applicazione. Ciò impedisce che JWT emessi per altre applicazioni vengano usati contro la vostra. - Implementare una Lista di Blocco (Deny List) (Opzionale): Per applicazioni critiche, considerare l'implementazione di una lista di blocco (nota anche come lista di revoca) per invalidare i JWT compromessi prima della loro scadenza. Questo aggiunge complessità ma può migliorare significativamente la sicurezza.
Esempio: Validazione delle Attestazioni nel Codice (Node.js con jsonwebtoken
)
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'https://example.com',
audience: 'https://myapp.com'
});
console.log(decoded);
} catch (error) {
console.error('Validazione del JWT fallita:', error);
}
3. Archiviare in Modo Sicuro i JWT sul Lato Client
Il modo in cui i JWT vengono archiviati sul lato client influisce in modo significativo sulla sicurezza:
- Evitare il Local Storage: Archiviare i JWT nel local storage li rende vulnerabili agli attacchi XSS. Se un aggressore riesce a iniettare JavaScript nella vostra applicazione, può facilmente rubare il JWT dal local storage.
- Usare Cookie HTTP-Only: Archiviare i JWT in cookie HTTP-only con gli attributi
Secure
eSameSite
. I cookie HTTP-only non possono essere accessibili da JavaScript, mitigando i rischi di XSS. L'attributoSecure
assicura che il cookie venga trasmesso solo su HTTPS. L'attributoSameSite
aiuta a prevenire attacchi CSRF. - Considerare i Token di Aggiornamento (Refresh Token): Implementare un meccanismo di token di aggiornamento. I token di accesso di breve durata vengono utilizzati per l'autorizzazione immediata, mentre i token di aggiornamento di lunga durata vengono utilizzati per ottenere nuovi token di accesso. Archiviare i token di aggiornamento in modo sicuro (es. in un database con crittografia).
- Implementare Protezione CSRF: Quando si utilizzano i cookie, implementare meccanismi di protezione CSRF, come i token di sincronizzazione o il pattern Double Submit Cookie.
Esempio: Impostazione di Cookie HTTP-Only (Node.js con Express)
app.get('/login', (req, res) => {
// ... logica di autenticazione ...
const token = jwt.sign({ userId: user.id }, privateKey, { expiresIn: '15m' });
const refreshToken = jwt.sign({ userId: user.id }, refreshPrivateKey, { expiresIn: '7d' });
res.cookie('accessToken', token, {
httpOnly: true,
secure: true, // Impostare a true in produzione
sameSite: 'strict', // o 'lax' a seconda delle necessità
maxAge: 15 * 60 * 1000 // 15 minuti
});
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true, // Impostare a true in produzione
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 giorni
});
res.send({ message: 'Login effettuato con successo' });
});
4. Proteggersi dagli Attacchi di "Algorithm Confusion"
La confusione di algoritmo è una vulnerabilità critica. Ecco come prevenirla:
- Specificare Esplicitamente gli Algoritmi Consentiti: Quando si verificano i JWT, specificare esplicitamente gli algoritmi di firma consentiti. Non fare affidamento sulla libreria JWT per determinare automaticamente l'algoritmo.
- Non Fidarsi dell'Header
alg
: Non fidarsi mai ciecamente dell'headeralg
nel JWT. Validarlo sempre rispetto a una lista predefinita di algoritmi consentiti. - Usare Tipizzazione Statica Forte (Se Possibile): Nei linguaggi che supportano la tipizzazione statica, applicare un controllo rigoroso dei tipi per i parametri della chiave e dell'algoritmo.
Esempio: Prevenire la Confusione di Algoritmo (Node.js con jsonwebtoken
)
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'] // Permetti esplicitamente solo RS256
});
console.log(decoded);
} catch (error) {
console.error('Validazione del JWT fallita:', error);
}
5. Implementare Meccanismi Corretti di Scadenza e Aggiornamento dei Token
La durata del token è una considerazione chiave per la sicurezza:
- Usare Token di Accesso di Breve Durata: Mantenere i token di accesso con una vita breve (es. 5-30 minuti). Ciò limita l'impatto in caso di compromissione di un token.
- Implementare i Token di Aggiornamento: Usare i token di aggiornamento per ottenere nuovi token di accesso senza richiedere all'utente di autenticarsi nuovamente. I token di aggiornamento possono avere una durata maggiore ma devono essere archiviati in modo sicuro.
- Implementare la Rotazione dei Token di Aggiornamento: Ruotare i token di aggiornamento ogni volta che viene emesso un nuovo token di accesso. Questo invalida il vecchio token di aggiornamento, limitando il potenziale danno se un token di aggiornamento viene compromesso.
- Considerare la Gestione delle Sessioni: Per applicazioni sensibili, considerare l'implementazione della gestione delle sessioni lato server in aggiunta ai JWT. Ciò consente di revocare l'accesso in modo più granulare.
6. Proteggersi dal Furto di Token
Prevenire il furto di token è fondamentale:
- Implementare una Content Security Policy (CSP) Rigorosa: Usare una CSP per prevenire attacchi XSS. La CSP consente di specificare quali fonti sono autorizzate a caricare risorse (script, stili, immagini, ecc.) sul vostro sito web.
- Sanificare l'Input dell'Utente: Sanificare tutti gli input dell'utente per prevenire attacchi XSS. Utilizzare una libreria di sanificazione HTML affidabile per eseguire l'escape di caratteri potenzialmente dannosi.
- Usare HTTPS: Usare sempre HTTPS per crittografare la comunicazione tra il client e il server. Questo impedisce agli aggressori di intercettare il traffico di rete e rubare i JWT.
- Implementare HSTS (HTTP Strict Transport Security): Usare HSTS per istruire i browser a utilizzare sempre HTTPS quando comunicano con il vostro sito web.
7. Monitoraggio e Registrazione (Logging)
Un monitoraggio e una registrazione efficaci sono essenziali per rilevare e rispondere agli incidenti di sicurezza:
- Registrare l'Emissione e la Validazione dei JWT: Registrare tutti gli eventi di emissione e validazione dei JWT, inclusi l'ID utente, l'indirizzo IP e il timestamp.
- Monitorare Attività Sospette: Monitorare modelli insoliti, come tentativi di accesso falliti multipli, JWT utilizzati da posizioni diverse contemporaneamente o richieste rapide di aggiornamento dei token.
- Impostare Avvisi: Impostare avvisi per notificarvi potenziali incidenti di sicurezza.
- Rivedere Regolarmente i Log: Rivedere regolarmente i log per identificare e investigare attività sospette.
8. Limitazione della Frequenza (Rate Limiting)
Implementare la limitazione della frequenza per prevenire attacchi di forza bruta e attacchi di denial-of-service (DoS):
- Limitare i Tentativi di Accesso: Limitare il numero di tentativi di accesso falliti da un singolo indirizzo IP o account utente.
- Limitare le Richieste di Aggiornamento dei Token: Limitare il numero di richieste di aggiornamento dei token da un singolo indirizzo IP o account utente.
- Limitare le Richieste API: Limitare il numero di richieste API da un singolo indirizzo IP o account utente.
9. Mantenersi Aggiornati
- Mantenere le Librerie Aggiornate: Aggiornare regolarmente le librerie JWT e le dipendenze per correggere le vulnerabilità di sicurezza.
- Seguire le Best Practice di Sicurezza: Rimanere informati sulle ultime best practice di sicurezza e sulle vulnerabilità relative ai JWT.
- Eseguire Audit di Sicurezza: Eseguire regolarmente audit di sicurezza della vostra applicazione per identificare e risolvere potenziali vulnerabilità.
Considerazioni Globali per la Sicurezza dei JWT
Quando si implementano JWT per applicazioni globali, considerare quanto segue:
- Fusi Orari: Assicurarsi che i propri server siano sincronizzati con una fonte di tempo affidabile (es. NTP) per evitare problemi di sfasamento del clock che possono influenzare la validazione dei JWT, in particolare le attestazioni
exp
enbf
. Considerare l'uso coerente di timestamp UTC. - Regolamenti sulla Privacy dei Dati: Essere consapevoli dei regolamenti sulla privacy dei dati, come GDPR, CCPA e altri. Ridurre al minimo la quantità di dati personali archiviati nei JWT e garantire la conformità con le normative pertinenti. Crittografare le attestazioni sensibili se necessario.
- Internazionalizzazione (i18n): Quando si visualizzano informazioni dalle attestazioni JWT, assicurarsi che i dati siano localizzati correttamente per la lingua e la regione dell'utente. Ciò include la formattazione appropriata di date, numeri e valute.
- Conformità Legale: Essere consapevoli di eventuali requisiti legali relativi all'archiviazione e alla trasmissione dei dati in diversi paesi. Assicurarsi che l'implementazione dei JWT sia conforme a tutte le leggi e i regolamenti applicabili.
- Cross-Origin Resource Sharing (CORS): Configurare correttamente il CORS per consentire alla propria applicazione di accedere a risorse da domini diversi. Questo è particolarmente importante quando si utilizzano JWT per l'autenticazione tra diversi servizi o applicazioni.
Conclusione
I JWT offrono un modo comodo ed efficiente per gestire l'autenticazione e l'autorizzazione, ma introducono anche potenziali rischi per la sicurezza. Seguendo queste best practice, è possibile ridurre significativamente il rischio di vulnerabilità e garantire la sicurezza delle proprie applicazioni globali. Ricordate di rimanere informati sulle ultime minacce alla sicurezza e di aggiornare la vostra implementazione di conseguenza. Dare priorità alla sicurezza durante l'intero ciclo di vita dei JWT aiuterà a proteggere i vostri utenti e i dati da accessi non autorizzati.