f in x
JWT Sicuro — Come Firmare e Verificare Token Senza Cadere nelle Vulnerabilità Comuni
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Sicurezza Informatica

JWT Sicuro — Come Firmare e Verificare Token Senza Cadere nelle Vulnerabilità Comuni

[2026-06-26] Author: Ing. Calogero Bono
Zenithby Meteora Web Il sistema operativo della tua attività. Social, clienti, prenotazioni e fatture in un'unica piattaforma. Palestre, barber, professionisti. Scopri Zenith Demo gratis · senza carta

Hai appena implementato l'autenticazione JWT nella tua API. I test passano, il frontend riceve il token, tutto sembra funzionare. Poi, per sbaglio, provi a modificare il payload del token e lo rispedisci al server. E il server lo accetta. Se non verifichi la firma, qualunque attaccante può impersonare qualsiasi utente.

Noi, di Meteora Web, vediamo questo errore nei progetti che ereditiamo: JWT spacciati per sicuri, ma senza alcun controllo reale sulla provenienza. Veniamo dalla contabilità e dal codice: sappiamo che un baco nella verifica della firma è come un buco nei libri contabili. Prima o poi viene fuori, e costa caro.

In questa guida ti mostriamo come firmare e verificare i JWT in modo robusto, e soprattutto quali vulnerabilità evitare. Tutto con esempi reali in PHP e Node.js, pronti da copiare e testare.

Come funziona la firma dei JWT e perché è critica per la sicurezza

Un JWT è composto da tre parti separate da punti: header, payload, signature. La signature è ciò che garantisce l'integrità del token. Senza di essa, chiunque può modificare il payload (es. cambiare il ruolo da "user" a "admin") e il server non se ne accorge.

La firma si genera applicando un algoritmo di hashing (HMAC o RSA/ECDSA) a header + payload + una chiave segreta (HMAC) o una coppia di chiavi (asimmetrico). Il server, quando riceve il token, ricalcola la firma con la stessa chiave e confronta. Se non corrisponde, rifiuta il token.

Algoritmi supportati: una scelta che fa la differenza

I JWT supportano molti algoritmi: HS256, HS384, HS512 (HMAC con SHA-2), RS256, RS384, RS512 (RSA con SHA-2), ES256, ES384, ES512 (ECDSA).

Sponsored Protocol

La scelta tra simmetrico (HMAC) e asimmetrico (RSA/ECDSA) dipende dal contesto:

  • HMAC: stessa chiave per firmare e verificare. Ideale quando client e server sono nello stesso dominio (unica applicazione). La chiave deve rimanere segreta sul server.
  • RSA/ECDSA: chiave privata per firmare, chiave pubblica per verificare. Perfetto se il token viene emesso da un server di autenticazione e verificato da servizi diversi (microservizi, API pubbliche).

Errore comune: usare HMAC con la stessa chiave per emettere e verificare token, ma esporre la chiave in un client nativo o JavaScript. Chiunque estrae la chiave e genera token validi.

Quali sono le vulnerabilità più comuni dei JWT

Le vulnerabilità sui JWT non sono astratte: sono sfruttate ogni giorno in attacchi reali. Ecco le più frequenti.

Algorithm confusion attack

L'attaccante modifica l'header del JWT impostando "alg": "none". Se il server non verifica esplicitamente che l'algoritmo sia tra quelli consentiti, accetta token senza firma. Variante: cambia da RS256 a HS256. Se il server si aspetta chiave pubblica (RSA) ma riceve HMAC, usa la chiave pubblica (spesso nota) per verificare la firma HMAC. L'attaccante firma il token con la chiave pubblica e il server lo approva.

Come proteggersi: non accettare mai l'algoritmo dall'input. Imposta una whitelist fissa nel codice.

Key confusion attack

Simile al precedente, ma focalizzato sullo scambio di chiavi. Se il server usa la stessa variabile per chiave HMAC e chiave pubblica, un attaccante può forzare l'uso di una chiave pubblica nota per firmare token HMAC.

Sponsored Protocol

Missing signature verification

Sembra banale, ma capita. Librerie con API che restituiscono il payload direttamente senza verificare la firma. Ad esempio, jwt.decode() in Python senza specificare verify=True. In Node.js, la funzione jwt.verify() va usata, non jwt.decode().

Token replay e scadenza non controllata

Un token valido può essere riutilizzato indefinitamente se non si imposta exp e se non si implementa un blacklist per token revocati. In più, se nbf (not before) non viene controllato, token futuri vengono accettati.

Come verificare la firma di un JWT in modo robusto

Vediamo implementazioni concrete. Partiamo dallo scenario più comune: server che emette e verifica token (HMAC). Poi passiamo al caso asimmetrico.

PHP con firebase/php-jwt

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// FIRMA (emissione)
$key = 'una-chiave-segreta-molto-lunga-e-casuale!';
$payload = [
    'sub' => 123,
    'name' => 'Mario Rossi',
    'iat' => time(),
    'exp' => time() + 3600
];
$jwt = JWT::encode($payload, $key, 'HS256');

// VERIFICA
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
print_r($decoded);
// Se la firma non è valida o il token è scaduto, JWT::decode lancia un'eccezione.

Attenzione: JWT::decode accetta un array di chiavi oppure un singolo oggetto Key. Specifica sempre l'algoritmo nel secondo parametro. Non usare mai JWT::decode($jwt, $key) senza Key, perché accetta qualsiasi algoritmo fino alla versione 5.x (vulnerabilità nota).

Sponsored Protocol

Node.js con jsonwebtoken

const jwt = require('jsonwebtoken');

// FIRMA
const secret = 'una-chiave-segreta-molto-lunga';
const token = jwt.sign(
  { sub: 123, name: 'Mario Rossi' },
  secret,
  { algorithm: 'HS256', expiresIn: '1h' }
);

// VERIFICA
jwt.verify(token, secret, { algorithms: ['HS256'] }, (err, decoded) => {
  if (err) {
    console.error('Token non valido:', err.message);
    return;
  }
  console.log(decoded);
});

Regola chiave: nell'opzione algorithms specifica solo gli algoritmi che accetti. Se non lo fai, la libreria accetta di default qualsiasi algoritmo, esponendoti all'alg confusion attack.

Caso asimmetrico (RS256)

Se usi coppia di chiavi, la firma avviene con la chiave privata e la verifica con quella pubblica. In PHP:

$privateKey = file_get_contents('/path/to/private.pem');
$publicKey = file_get_contents('/path/to/public.pem');

// FIRMA
$jwt = JWT::encode($payload, $privateKey, 'RS256');

// VERIFICA
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));

In Node.js:

const privateKey = fs.readFileSync('private.pem');
const publicKey = fs.readFileSync('public.pem');

const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
jwt.verify(token, publicKey, { algorithms: ['RS256'] });

Mai mischiare chiavi simmetriche e asimmetriche nello stesso progetto. Usa un unico approccio per dominio.

Sponsored Protocol

Come proteggere i JWT da attacchi di tipo alg confusion e key confusion

La difesa è una combinazione di codice solido e buone pratiche architetturali.

Whitelist fissa degli algoritmi

Nel codice, non leggere mai l'algoritmo dall'header del token per decidere come verificare. Imposta una costante o variabile d'ambiente con la lista degli algoritmi accettati.

$allowed_algs = ['HS256']; // oppure ['RS256']

E passa sempre questa lista alla verifica.

Validazione del token a più livelli

  1. Struttura: verifica che il token abbia esattamente tre parti separate da punti.
  2. Header: controlla che typ sia JWT e che l'alg sia nella whitelist.
  3. Payload: verifica i claim standard (exp, nbf, iat, iss, aud).
  4. Firma: ricalcola la firma con la chiave e l'algoritmo atteso.

Alcune librerie fanno già tutto, ma se usi una libreria minimale, implementa tu questi passi.

Gestione sicura delle chiavi

  • Mai hardcodare chiavi nel codice. Usa variabili d'ambiente o vault.
  • Ruota periodicamente le chiavi HMAC o le coppie RSA/ECDSA.
  • Per le chiavi asimmetriche, non esporre la chiave privata. La chiave pubblica può essere distribuita, ma verificane la provenienza (certificato, JWKS endpoint).

Impedire il replay token

Oltre a exp (breve, 15-60 minuti), implementa un meccanismo di revoca per token blacklistati (es. logout, cambio password). Un approccio semplice: tieni un ID univoco nel token (jti) e memorizza in un database (Redis con TTL) i token revocati. Ad ogni richiesta, controlla se il jti è nella blacklist.

Sponsored Protocol

$blacklisted = $redis->get('blacklist:' . $decoded->jti);
if ($blacklisted) {
    throw new Exception('Token revocato');
}

Cosa fare adesso

Abbiamo visto che un JWT è sicuro solo se la firma viene verificata con l'algoritmo giusto e la chiave corretta. Basta un dettaglio — un decode() al posto di verify(), un algoritmo non forzato — per aprire la porta a un attacco.

Azioni immediate da compiere oggi:

  1. Controlla il tuo codice di verifica: stai usando jwt.verify() o JWT::decode con Key? Se usi una libreria senza specificare l'algoritmo, correggi subito.
  2. Aggiungi una whitelist di algoritmi nel tuo middleware di autenticazione. Non lasciare che sia l'header del token a decidere.
  3. Implementa la blacklist dei token revocati con Redis o database. Soprattutto se hai logout attivo o cambio password frequente.
  4. Rivedi la durata dei token: exp troppo lungo aumenta il rischio di furto. Punta a 15-60 minuti per access token, e usa refresh token con rotazione.
  5. Esegui un audit di sicurezza su tutto il flusso JWT: dal server di emissione ai client che ricevono il token. Noi, di Meteora Web, lo facciamo regolarmente sui progetti che ci vengono affidati. Se vuoi un check approfondito, contattaci.

Per approfondire la sicurezza a 360°, leggi anche la nostra guida pillar su crittografia e sicurezza dati e la guida sul vulnerability scanning per individuare falle nella tua infrastruttura.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()