Le moderne applicazioni web si basano sempre più su token JWT (JSON Web Token) come meccanismo di autenticazione principale. Nell'ambiente Next.js, che combina il rendering lato server (SSR) e lato client (CSR), una corretta gestione della sessione richiede un'architettura ben pensata. Questo articolo presenta il modello di minaccia, descrive il funzionamento del meccanismo, mostra il contrasto tra codice vulnerabile e corretto e fornisce una checklist di mitigazione conforme agli standard OWASP, CWE e al principio del least‑privilege.
Modello di minaccia – cosa può andare storto?
I principali vettori di attacco ai sistemi basati su JWT sono:
- furto del token tramite XSS (uno script iniettato nel browser legge il token dalla memoria locale o da un cookie non protetto);
- attacco CSRF, quando il browser invia automaticamente il cookie con il token nella richiesta all'API;
- replay attack – il token intercettato viene riutilizzato prima della scadenza;
- assenza di rotazione delle chiavi – in caso di perdita è possibile bloccare tutti i token in una sola volta.
Questo modello può essere visualizzato come tre livelli: memorizzazione del token, sua verifica e gestione del ciclo di vita. Ogni livello richiede protezioni specifiche.
Come funziona il meccanismo JWT in Next.js?
Il flusso tipico è il seguente:
- L'utente effettua il login, il server (Node.js) genera due token –
accessToken(breve durata) erefreshToken(più lungo). - I token vengono inviati al browser in cookie
HttpOnlyeSecure. - Durante una richiesta API il browser aggiunge automaticamente
accessToken. Il middleware di Next.js verifica la firma e la data di scadenza. - Quando
accessTokenscade, l'applicazione chiama l'endpoint di refresh, fornendorefreshToken. Il server restituisce un nuovoaccessTokene, opzionalmente, un nuovorefreshToken.
È fondamentale che i token non siano accessibili da JavaScript (da qui HttpOnly) e che ogni endpoint richieda la verifica della firma usando la chiave corrente.
Esempio: codice vulnerabile vs. codice corretto
Di seguito confrontiamo due snippet – prima un metodo semplice ma pericoloso di memorizzare il token in localStorage, poi l'implementazione consigliata con cookie HttpOnly e middleware in Next.js.
// VULNERABILE – memorizzazione in localStorage (soggetto a XSS)
function login(credentials) {
fetch('/api/auth/login', {method: 'POST', body: JSON.stringify(credentials)})
.then(r => r.json())
.then(data => {
localStorage.setItem('accessToken', data.accessToken);
localStorage.setItem('refreshToken', data.refreshToken);
});
}
// CORRETTO – impostazione di cookie HttpOnly lato server
// pages/api/auth/login.js
import { sign } from 'jsonwebtoken';
export default async function handler(req, res) {
const { username, password } = req.body;
// ... verifica delle credenziali ...
const accessToken = sign({ sub: username }, process.env.JWT_ACCESS_SECRET, { expiresIn: '15m' });
const refreshToken = sign({ sub: username }, process.env.JWT_REFRESH_SECRET, { expiresIn: '30d' });
res.setHeader('Set-Cookie', [
`accessToken=${accessToken}; HttpOnly; Secure; SameSite=Strict; Path=/api; Max-Age=900`,
`refreshToken=${refreshToken}; HttpOnly; Secure; SameSite=Strict; Path=/api/auth/refresh; Max-Age=2592000`
]);
res.status(200).json({ message: 'Logged in' });
}
Nella versione corretta i token non arrivano a JavaScript, e i flag SameSite=Strict riducono il rischio di CSRF. Inoltre, limitiamo il percorso (Path) in modo che i cookie vengano inviati solo agli endpoint dedicati.
Checklist di mitigazione – cosa fare per soddisfare OWASP e CWE
- Memorizzazione: utilizza cookie
HttpOnly+Secure; evitalocalStorageesessionStorageper i token. - Limitazione di dominio e percorso: imposta
DomainePathaffinché il cookie sia disponibile solo per l'API. - SameSite: scegli
StrictoLaxa seconda delle necessità, per proteggere da CSRF. - Validazione: verifica la firma, la data di scadenza e i claim (
iss,aud) sul server. - Rotazione delle chiavi: cambia regolarmente i segreti JWT; conserva la chiave precedente per un breve periodo per consentire la verifica dei token esistenti.
- Refresh dei token: utilizza
accessTokenbrevi erefreshTokenpiù lunghi; dopo un refresh riuscito invalida il refresh token precedente. - Rate limiting e monitoraggio delle verifiche fallite – riduce il rischio di brute‑force e token‑replay.
L'implementazione dei punti precedenti risponde a CWE‑287 (Improper Authentication) e CWE‑352 (Cross‑Site Request Forgery).
Strategia di rotazione delle chiavi JWT
Le chiavi usate per firmare i token dovrebbero avere una durata definita (ad es. 30 giorni). Il processo di rotazione comprende:
- Generare una nuova chiave e salvarla in un archivio sicuro (ad es. AWS KMS, HashiCorp Vault).
- Aggiungere la nuova chiave all'elenco delle “accettate” nella configurazione dell'applicazione, mantenendo la chiave precedente come “fallback”.
- Dopo il periodo di transizione (ad es. 24 h) rimuovere la chiave vecchia – tutti i token firmati con la chiave più vecchia non saranno più accettati.
In Next.js è possibile realizzarlo con una funzione di supporto:
// utils/jwt.js
import jwt from 'jsonwebtoken';
const keys = {
current: process.env.JWT_ACCESS_SECRET,
previous: process.env.JWT_ACCESS_SECRET_OLD,
};
export function verify(token) {
try {
return jwt.verify(token, keys.current);
} catch (e) {
// fallback to previous key
return jwt.verify(token, keys.previous);
}
}
Questo approccio soddisfa il requisito OWASP “Key Management” e minimizza il rischio di interruzione della sessione durante la rotazione.
Errori comuni e compromessi
Even experienced teams make mistakes. I più frequenti sono:
- Impostare una durata troppo lunga per l'access token – aumenta la superficie di attacco in caso di furto.
- Mancanza di refresh del refresh token – consente l'uso illimitato di un token rubato.
- Utilizzare una sola chiave per access e refresh token – rende più difficile la rotazione e aumenta le conseguenze di una fuga.
- Flag SameSite inappropriati – con
Nonei token sono esposti a CSRF in contesti cross‑site. - Esposizione dei token nei log – soprattutto negli ambienti CI/CD.
Soluzioni di compromesso, come il “sliding expiration” per il refresh token, possono aiutare, ma è sempre necessario valutare l'impatto sulla sicurezza e sull'usabilità.
La migliore difesa non è solo la tecnologia, ma la progettazione consapevole delle sessioni fin dall'inizio.
Checklist pratica di implementazione
- ✅ Usa
HttpOnly,SecureeSameSite=Strictquando imposti i cookie con JWT. - ✅ Limita il
Pathdei cookie agli endpoint API. - ✅ Implementa middleware in Next.js che verifica il token ad ogni richiesta.
- ✅ Crea un endpoint di refresh dei token con rotazione del refresh token.
- ✅ Applica la rotazione delle chiavi ogni 30‑60 giorni, mantenendo la chiave precedente come fallback.
- ✅ Monitora le verifiche fallite e applica il rate limiting.
- ✅ Revisiona regolarmente il codice alla ricerca di XSS e rimuovi tutte le inserzioni di dati pericolose.
Seguendo questa lista, il team di ingegneria costruisce una solida base di sicurezza delle sessioni, conforme agli standard OWASP Top 10 e CWE‑798 (Use of Hard‑coded Credentials).
In sintesi, i token JWT sicuri in Next.js richiedono una strategia di archiviazione ponderata, una rotazione regolare delle chiavi e un controllo rigoroso sul ciclo di vita dei token. Implementando le pratiche descritte, ridurrai il rischio di furto di token, attacchi CSRF e XSS, garantendo al contempo la conformità ai migliori standard del settore. Se hai bisogno di supporto per un audit di sicurezza o per l'implementazione di questo sistema nella tua organizzazione, contatta Coderia.it – ti aiuteremo a costruire una soluzione che soddisfi i più alti requisiti di sicurezza e performance.



