JWT vs Session: quale meccanismo di autenticazione scegliere?
Autenticare un utente significa sapere a ogni richiesta chi è. Si contrappongono due grandi famiglie: le sessioni server, in cui il server tiene traccia dell'utente connesso, e i JSON Web Token (JWT), in cui l'identità viaggia in un token firmato portato dal client. La scelta influisce sulla sicurezza, sulla capacità di scalare e sulla facilità di disconnettere un utente. Ecco come decidere in base al tuo contesto.
Le sessioni server (stateful)
Con una sessione, il server crea un identificativo di sessione al momento della connessione, memorizza i dati associati (identità, ruoli, carrello) lato server (memoria, Redis, database) e restituisce al browser un cookie contenente soltanto questo identificativo. A ogni richiesta, il server legge il cookie, ritrova la sessione e sa chi sta parlando.
- Stato lato server: la fonte di verità resta sul server, il client porta solo un riferimento opaco.
- Revoca immediata: eliminare la sessione lato server disconnette istantaneamente l'utente.
- Cookie: trasmesso automaticamente dal browser, idealmente in
HttpOnly,SecureeSameSite.
I JSON Web Token (stateless)
Un JWT è un token autoportante composto da tre parti codificate in base64url e separate da punti: un header, un payload (i claim, ad esempio l'identificativo utente e la scadenza) e una firma. Il server firma il token alla connessione; in seguito gli basta verificare la firma per fidarsi del contenuto, senza memorizzare nulla.
- Senza stato: tutte le informazioni necessarie sono nel token, il server non ha bisogno di memoria condivisa.
- Verificabile ovunque: qualsiasi servizio che conosca la chiave può validare il token, comodo per le architetture distribuite e il SSO.
- Strumenti: puoi ispezionare un token con il nostro decoder JWT, controllarne la firma con il verificatore JWT o forgiarne uno con il generatore JWT.
Tabella comparativa
| Criterio | Sessione server | JWT |
|---|---|---|
| Stato | Stateful (memorizzato sul server) | Stateless (portato dal client) |
| Archiviazione server | Necessaria (Redis, database) | Nessuna |
| Revoca | Immediata | Difficile prima della scadenza |
| Scalabilità orizzontale | Store condiviso necessario | Nativa |
| Dimensione trasmessa | Piccola (un identificativo) | Maggiore (claim firmati) |
| Cross-dominio / SSO | Vincolante | Adatto |
| Superficie XSS | Bassa se cookie HttpOnly | Elevata se memorizzato in localStorage |
Sicurezza: XSS, CSRF e revoca
Entrambi gli approcci sono sicuri se ben implementati, ma i loro rischi differiscono.
- XSS: un cookie di sessione
HttpOnlyè inaccessibile a JavaScript, quindi protetto dal furto tramite injection. Un JWT memorizzato inlocalStorageè invece leggibile da qualsiasi script, il che ne fa un bersaglio privilegiato. Memorizzare il JWT in un cookieHttpOnlyannulla questo vantaggio del JWT ma reintroduce il rischio CSRF. - CSRF: i cookie vengono inviati automaticamente, quindi sono vulnerabili al CSRF senza protezione (attributo
SameSite, token anti-CSRF). Un JWT inviato manualmente nell'headerAuthorizationnon è interessato. - Revoca: è il punto debole del JWT. Essendo autoportante, non lo si può invalidare prima della scadenza senza reintrodurre uno stato server (lista di revoca, blacklist). Una sessione si elimina istantaneamente.
Scalabilità e architettura
Su un singolo server, le sessioni sono banali. Non appena distribuisci il carico su più istanze, ogni istanza deve accedere alle sessioni: serve uno store condiviso (Redis) o delle sticky session. Il JWT brilla qui, perché qualsiasi istanza valida il token senza chiamate di rete né archiviazione comune.
- Microservizi: un JWT propaga l'identità da un servizio all'altro senza database centrale.
- API pubbliche e mobile: il JWT evita la gestione dei cookie lato client nativo.
- Monolite classico: la sessione resta più semplice e più sicura per impostazione predefinita.
Quando scegliere l'uno o l'altro
Scegliere le sessioni quando
- Sviluppi un'applicazione web classica con rendering lato server
- La revoca immediata è critica (banca, sanità, back-office)
- Vuoi la soluzione più sicura per impostazione predefinita, con meno insidie
- La tua infrastruttura regge uno store di sessioni condiviso senza problemi
Scegliere il JWT quando
- Esponi un'API consumata da SPA, mobile o terze parti
- Hai un'architettura a microservizi o un SSO tra domini
- Devi scalare orizzontalmente senza store condiviso
- Accetti di gestire la scadenza breve e il rinnovo dei token
Raccomandazione
Per la maggior parte delle applicazioni web, le sessioni server restano la scelta più sicura e più semplice: revoca immediata, cookie HttpOnly e zero gestione di token lato client. Riserva il JWT ai casi in cui la sua assenza di stato porta un valore reale: API stateless, mobile, microservizi, SSO.
Se opti per il JWT, mantieni una durata di vita breve (qualche minuto) abbinata a un refresh token memorizzato in un cookie HttpOnly, e prevedi una lista di revoca per i casi sensibili. Combini così il meglio dei due mondi.
Domande frequenti
Un JWT è cifrato?
No, per impostazione predefinita un JWT è soltanto firmato, non cifrato. Il suo payload è codificato in base64url e leggibile da chiunque lo intercetti. Non inserire mai dati sensibili in chiaro in un JWT. Per cifrare il contenuto occorre ricorrere a JWE (JSON Web Encryption).
Dove memorizzare un JWT lato client?
Il modo più sicuro è un cookie HttpOnly, Secure e SameSite, che protegge dal furto tramite XSS. Il localStorage è più semplice ma espone il token a qualsiasi script malevolo. Evitalo per i token ad alto privilegio.
Come disconnettere un utente con un JWT?
Essendo il token autoportante, la disconnessione reale richiede o di attendere la sua scadenza, o di mantenere una lista di revoca lato server. Per questo si usano durate di vita brevi e un refresh token che, invece, può essere revocato.
Si possono combinare sessioni e JWT?
Sì, è una pratica comune: un access token JWT a breve durata per le chiamate API, e un refresh token gestito come una sessione (memorizzato e revocabile lato server) per rinnovare l'access token.