Segui su:
Programmazione Web Italia

Scopri i JSON Web Token (JWT) - autenticazione stateless per le tue applicazioni web

Scopri i JSON Web Token (JWT) - autenticazione stateless per le tue applicazioni web

In termini di sicurezza e scalabilità, i JSON Web Token (JWT) emergono come la chiave per garantire un’autenticazione sicura, efficiente e senza soluzione di continuità. Scopri come questa tecnologia può rivoluzionare il modo in cui gestisci l’accesso alle tue risorse online! Marco dice...

Introduzione

Cos’è e a cosa serve un JWT

Nel mondo dello sviluppo web, la sicurezza e la gestione dell’autenticazione sono aspetti fondamentali: uno degli strumenti più utilizzati per garantire un accesso sicuro e scalabile nelle applicazioni moderne è rappresentato dal JWT (JSON Web Token).

Un JWT è un token che permette di scambiare informazioni in modo sicuro tra due parti (tipicamente un client ed un server). Grazie alla sua struttura basata su JSON e alla possibilità di essere firmato digitalmente, i JWT vengono spesso utilizzati per autenticazione e autorizzazione in applicazioni web e API.

Rispetto ai tradizionali metodi basati sulle sessioni, i JSON Web Token offrono un approccio stateless, riducendo il carico sui server, che non dovranno più salvare i dati di sessione, e migliorando la scalabilità delle applicazioni. I JWT, leggeri e compatti, sono una soluzione particolarmente adatta per architetture moderne e distribuite, come le applicazioni single-page e i sistemi microservices.

Contesto d’uso: autenticazione e autorizzazione nelle applicazioni web e API

I JSON Web Token (JWT) sono ampiamente utilizzati per gestire l’autenticazione e l’autorizzazione in applicazioni web e API. Questi due concetti, sebbene correlati, hanno ruoli distinti nella sicurezza di un sistema.

  • Autenticazione: verifica l’identità di un utente. Dopo il login, un server genera un JWT e lo invia al client, che lo utilizza per autenticarsi nelle richieste successive.
  • Autorizzazione: determina quali risorse un utente autenticato può accedere. Il JWT può contenere informazioni sui permessi dell’utente, consentendo ai server di verificare rapidamente i diritti di accesso senza consultare un database.

I JSON Web Token (JWT) sono ampiamente utilizzati per gestire l’autenticazione nelle applicazioni web e API. Dopo che un utente si autentica con successo (ad esempio, fornendo username e password), il server genera un JWT e lo invia al client. Da quel momento, il client include il token nelle richieste successive per dimostrare la propria identità senza dover effettuare nuovamente il login.

Oltre all’autenticazione, i JWT possono essere utilizzati anche per l’autorizzazione. Un JWT può contenere informazioni sui ruoli e i permessi dell’utente, permettendo al server di verificare cosa l’utente è autorizzato a fare. Ad esempio, un’API potrebbe concedere accesso a determinati endpoint solo se nel token è presente un ruolo specifico (es. admin).

Differenza tra session-based authentication e token-based authentication

Quando si implementa un sistema di autenticazione in un’applicazione web, si può scegliere tra due approcci principali: session-based authentication e token-based authentication. Entrambi hanno lo scopo di identificare e mantenere l’autenticazione di un utente, ma funzionano in modo molto diverso.

Session-Based Authentication (Autenticazione basata su sessione)

È il metodo tradizionale utilizzato nei sistemi basati su server. I passaggi che comporta sono i seguenti.

  • L’utente si autentica inviando le proprie credenziali (es. username e password)
  • Il server verifica le credenziali e crea una sessione, salvando le informazioni dell’utente in memoria o in un database
  • Al client viene assegnato un session ID, che viene memorizzato in un cookie e inviato con ogni richiesta successiva
  • Il server verifica il session ID per autenticare l’utente e restituire le risorse richieste
  • Quando l’utente esce, la sessione viene invalidata lato server.

Token-Based Authentication (Autenticazione basata su token: JWT)

Questo metodo elimina la necessità di mantenere uno stato lato server. I passaggi che comporta sono i seguenti.

  • L’utente si autentica inviando le proprie credenziali
  • Il server verifica le credenziali e genera un JWT, che contiene informazioni sull’utente e una firma crittografica
  • Il client memorizza il JWT (di solito nel local storage o nei cookie) e lo invia con ogni richiesta HTTP nell’Authorization header
  • Il server verifica la firma del JWT e concede l’accesso alle risorse senza consultare un database
  • Quando il token scade, l’utente deve autenticarsi nuovamente o utilizzare un refresh token per ottenere un nuovo JWT

Cos'è un JSON Web Token (JWT)?

Definizione e standard di riferimento (RFC 7519)

Un JSON Web Token (JWT) è un formato compatto e sicuro per la trasmissione di informazioni tra parti diverse. È basato sullo standard RFC 7519 ed è ampiamente utilizzato per l’autenticazione e l’autorizzazione nelle applicazioni web e API. A differenza dei tradizionali session ID, un JWT è stateless, il che significa che non richiede al server di mantenere informazioni sulla sessione dell’utente. Tutti i dati necessari per l’autenticazione sono contenuti all’interno del token stesso.

Un JWT è composto da tre parti, separate da un punto (.).

  • Header (intestazione) - Contiene le informazioni sulla firma e il tipo di token
  • Payload (corpo) - Contiene le informazioni sull’utente e altri dati personalizzati. I dati nel payload non sono criptati, quindi chiunque abbia accesso al token può leggerli. Tuttavia, sono firmati digitalmente, quindi non possono essere modificati senza invalidare il token.
  • Signature (firma) - La firma assicura l’integrità del token e viene generata combinando l’header e il payload con una chiave segreta o una chiave privata

Attenzione: un errore comune è pensare che i JWT siano criptati. I dati all’interno del payload sono leggibili, quindi è fondamentale non includere informazioni sensibili (es. password, dati bancari).

Esempio di JWT

Ecco un esempio di JWT completo, suddiviso nelle sue tre parti: Header, Payload e Signature.

L'header specifica l'algoritmo di firma e il tipo di token

Esempio di JWT: Header

 
{
  "alg": "HS256",
  "typ": "JWT" 
}
   

Il payload contiene le informazioni dell'utente e altre claim (dichiarazioni), come la scadenza del token (exp), il suo tempo di creazione (iat), il ruolo dell'utente (role) e i'identificativo univoco (sub):

Esempio di JWT: Payload

 
{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "admin",
  "iat": 1710412800,
  "exp": 1710416400
}
  

La firma viene generata combinando Header + Payload, codificati in Base64Url, e firmati con una chiave segreta:

Esempio di JWT: Signature

 
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  "chiave-segreta"
)
 

Un JWT completo (header + payload + signature) avrà questo aspetto (notare che le tre sezioni sono separate da un punto (.)):

Esempio di JWT: il JWT risultante

 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzEwNDEyODAwLCJleHAiOjE3MTA0MTY0MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
 

Come funziona un JWT?

Un JSON Web Token (JWT) segue un flusso ben definito per l’autenticazione e l’autorizzazione degli utenti nelle applicazioni web e API. Il processo può essere suddiviso in tre fasi principali: generazione, trasmissione e verifica.

Processo di generazione del token

Quando un utente si autentica (ad esempio con email e password), il server verifica le credenziali e genera un JWT. Questo token contiene tipicamente le seguenti informazioni.

  • Informazioni sull’utente (es. ID, ruolo, nome)
  • Metadati (es. data di creazione, data di scadenza)
  • Firma crittografica, per garantirne l’integrità

Il server firma il JWT con una chiave segreta (o una chiave privata nel caso di firme asimmetriche come RSA).

Trasmissione del JWT

Una volta generato, il JWT viene inviato al client (browser o app mobile), che può memorizzarlo in:

  • Local Storage
  • Session Storage
  • HTTP-Only Cookie (più sicuro contro attacchi XSS)

Quando il client deve accedere a una risorsa protetta, include il JWT in ogni richiesta HTTP, tipicamente nell’Authorization header

Esempio di Authorization header con JWT allegato

 
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
 

Questo evita la necessità di mantenere una sessione lato server.

Meccanismo di verifica della firma

Quando il server riceve una richiesta con un JWT, ne esegue la verifica per concedere o negare l’accesso:

  • Decodifica il token e legge il payload.
  • Verifica la firma per assicurarsi che il token non sia stato modificato.
  • Controlla la scadenza (exp): se il token è scaduto, l’accesso viene negato.
  • Determina i permessi dell’utente in base ai dati contenuti nel payload.

Se il token è valido, il server concede l’accesso alla risorsa richiesta. In caso contrario, restituisce un errore 401 Unauthorized.

Vantaggi e svantaggi dell’utilizzo di JWT

L’uso dei JSON Web Token (JWT) per l’autenticazione e l’autorizzazione ha reso le applicazioni web e le API più scalabili ed efficienti, ma presenta anche alcune criticità da considerare. Analizziamo nel dettaglio i vantaggi e gli svantaggi di questa tecnologia.

Vantaggi di JWT

Stateless: nessuna gestione della sessione lato server

I JWT sono autocontenuti, ovvero contengono tutte le informazioni necessarie per identificare l’utente. Questo significa che il server non deve mantenere uno stato della sessione, riducendo la complessità e il consumo di memoria.

Scalabilità elevata

Poiché il server non deve memorizzare sessioni, JWT è ideale per applicazioni distribuite e microservizi, dove più istanze del server possono verificare il token senza dover condividere lo stato della sessione.

Sicurezza garantita dalla firma digitale

Ogni JWT è firmato digitalmente con un algoritmo HMAC (es. HS256) o asimmetrico (es. RS256). Questo impedisce che il token venga alterato senza essere invalidato.

Efficienza nel trasporto

I JWT sono in formato Base64Url, il che li rende facilmente trasportabili su HTTP (Authorization Header), WebSocket o persino in URL come parametri di query.

Supporto a Single Sign-On (SSO)

Poiché i JWT possono essere verificati senza contattare il server che li ha emessi, sono perfetti per l’autenticazione federata e il Single Sign-On (SSO) tra più servizi.

Flessibilità nel contenere informazioni personalizzate

Oltre agli ID utente, i JWT possono includere ruoli, permessi e metadati personalizzati, rendendo più facile la gestione dell’accesso.

Svantaggi di JWT

Non è possibile revocare un token lato server

Una volta emesso, un JWT non può essere invalidato fino alla sua scadenza, a meno che non si implementi una blacklist lato server (che però annulla il vantaggio dello stato stateless). Questo può essere un problema in caso di furto del token o logout forzato.

Dimensioni maggiori rispetto ai session ID

I JWT contengono più informazioni rispetto a un semplice session ID, e questo può aumentare il peso delle richieste HTTP, specialmente se usati nei cookie o negli header di ogni richiesta API.

Vulnerabilità a XSS e furto del token

Se un JWT viene salvato in Local Storage, un attacco Cross-Site Scripting (XSS) potrebbe rubarlo e permettere a un hacker di impersonare l’utente. È preferibile usare HTTP-Only Cookies per mitigare questo rischio.

Non sono criptati, solo firmati

I dati contenuti nel JWT sono facilmente leggibili con una semplice decodifica Base64. Se il token contiene informazioni sensibili, queste possono essere esposte se il token viene intercettato. È buona pratica non includere mai dati sensibili nel payload del JWT.

La gestione della scadenza richiede l’uso di Refresh Token

Quando un JWT scade (exp nel payload), l’utente deve eseguire nuovamente il login o ottenere un nuovo token tramite un Refresh Token, aggiungendo complessità all’implementazione.

Best Practices per l’utilizzo dei JWT

L’utilizzo di JSON Web Tokens (JWT) per l’autenticazione e l’autorizzazione nelle applicazioni web e nelle API può essere estremamente vantaggioso, ma è essenziale seguire alcune best practices per garantirne una gestione sicura ed efficace. Ecco le principali linee guida da adottare per sfruttare al meglio i JWT.

Utilizza HTTPS per tutte le comunicazioni

Poiché i JWT sono trasmessi come stringhe di testo attraverso la rete, è fondamentale proteggerli da eventuali attacchi di man-in-the-middle (MITM). E' sempre necessario utilizzare HTTPS per tutte le comunicazioni tra client e server. Questo meccanismo cripta i dati in transito e impedisce che i token vengano intercettati o alterati durante la trasmissione.

Non memorizzare dati sensibili nel payload

Poiché il payload di un JWT non è criptato, ma solo firmato, chiunque abbia accesso al token può decodificarlo e leggere i suoi contenuti. Non memorizzare mai dati sensibili come password, numeri di carte di credito o altre informazioni private nel payload. Usa il token solo per identificare l'utente o il suo stato di autenticazione, non per trasmettere dati riservati.

Usa una chiave segreta complessa

La chiave segreta utilizzata per firmare il JWT deve essere complessa e difficile da indovinare. Se stai usando un algoritmo simmetrico come HS256, una chiave segreta debole potrebbe esporre il sistema a attacchi di brute force. Se possibile, usa un algoritmo asimmetrico (ad esempio RS256), che usa una coppia di chiavi pubblica/privata, rendendo la gestione dei JWT più sicura.

Imposta una scadenza breve per i token

I JWT dovrebbero avere una durata limitata. Imposta un tempo di scadenza (exp) che sia abbastanza breve per ridurre i rischi di esposizione, ma non così breve da costringere l'utente a fare login troppo frequentemente. Ad esempio, un token potrebbe scadere dopo 15-30 minuti. Se desideri mantenere l'accesso senza obbligare un nuovo login, usa un Refresh Token per ottenere un nuovo JWT senza richiedere la reimmissione delle credenziali.

Usa il refresh token per ottenere nuovi JWT

Poiché i JWT sono stateless e non possono essere invalidati facilmente, puoi implementare un Refresh Token per consentire agli utenti di continuare ad accedere all'applicazione senza dover autenticarsi ripetutamente. Il Refresh Token può essere usato per ottenere un nuovo JWT quando quello precedente è scaduto, senza bisogno che l'utente fornisca nuovamente le credenziali. Tratta i Refresh Token con la stessa attenzione dei JWT, memorizzandoli in un luogo sicuro (preferibilmente in un cookie HTTP-Only).

Proteggi i JWT contro attacchi XSS e CSRF

Un JWT può essere vulnerabile a Cross-Site Scripting (XSS) se memorizzato in Local Storage o Session Storage, poiché uno script maligno potrebbe rubarlo. La soluzione migliore è memorizzare i JWT in un cookie HTTP-Only, che non è accessibile via JavaScript e riduce i rischi di attacchi XSS. Inoltre, quando usi JWT con i cookie, proteggi l’applicazione da attacchi Cross-Site Request Forgery (CSRF) utilizzando il flag SameSite sui cookie, che limita l'invio automatico dei cookie nelle richieste cross-site.

Verifica sempre la firma del JWT

Ogni volta che un server riceve un JWT, deve verificare la sua firma per assicurarsi che il token non sia stato modificato. La verifica della firma è fondamentale per proteggere l’integrità dei dati. Se usi un algoritmo simmetrico come HS256, verifica che il token sia stato firmato con la stessa chiave segreta usata per generarlo. Se usi un algoritmo asimmetrico come RS256, verifica che il token sia stato firmato con la chiave privata corrispondente alla chiave pubblica che possiedi.

Monitora i JWT e rileva abusi

Anche se i JWT sono sicuri se usati correttamente, è importante monitorare l'uso dei token. Ad esempio, tieni traccia dei tentativi di accesso falliti o di anomalie nel comportamento dei token (come richieste provenienti da indirizzi IP o dispositivi sospetti). Puoi utilizzare strumenti di logging e monitoring per rilevare abusi e implementare misure correttive, come la revoca del token in caso di comportamento sospetto.

Considera la gestione dei permessi

JWT può contenere dati sull’utente, ma non è una soluzione completa per la gestione dei permessi. Utilizza claim personalizzati come role o permissions per gestire l'accesso alle risorse, ma fai attenzione a non commettere l'errore di gestire l'autorizzazione basandoti solo sul contenuto del token. Controlla sempre i permessi sul server prima di eseguire azioni sensibili, per evitare che utenti non autorizzati possano accedere a risorse protette.

Conclusione

In questo articolo abbiamo esplorato il concetto di JSON Web Token (JWT), una tecnologia sempre più diffusa per l’autenticazione e l’autorizzazione nelle applicazioni moderne. Abbiamo visto come funziona, le sue potenzialità e i principali vantaggi che offre.

Tuttavia, come per ogni tecnologia, l'uso dei JWT presenta anche delle sfide. La gestione della sicurezza, la protezione contro attacchi come XSS o CSRF, la scadenza dei token e l’impossibilità di revocare i token in tempo reale sono aspetti che devono essere affrontati con attenzione. È fondamentale seguire le best practices che abbiamo discusso, come l'uso di HTTPS, l'adozione di Refresh Token per gestire la durata dei JWT e la protezione contro l’intercettazione dei token.

Con una corretta implementazione, i JWT possono essere una soluzione potente ed efficace per le moderne applicazioni web e le API, semplificando la gestione dell’autenticazione, aumentando la scalabilità e riducendo il carico sul server.

In conclusione, JWT rappresenta una scelta eccellente per applicazioni che necessitano di una soluzione leggera, sicura e facilmente integrabile per l'autenticazione e l’autorizzazione. Con un’attenzione adeguata alla sicurezza, questa tecnologia può rendere le tue applicazioni più sicure, scalabili e robuste.