1 25/10/2026 8 min

Il punto non è il comando in sé, ma il fatto che il valore ottenuto deve poi essere trattato come segreto operativo, non come dato da incollare in un file pubblico o in una documentazione interna non protetta.

Dove conservare il secret

La scelta migliore dipende dall’infrastruttura, ma la regola non cambia: il secret va fuori dal codice sorgente. In molti progetti piccoli si usa un file .env locale, mentre in produzione è meglio affidarsi a secret manager, variabili d’ambiente iniettate dal sistema di deploy o a soluzioni dedicate come vault, parameter store o gestori del cloud provider.

Un file .env può andare bene per sviluppo, a patto che non finisca nel repository e che sia escluso con .gitignore. In produzione, però, il rischio operativo aumenta: backup, accessi al filesystem, errori di permessi e copie involontarie possono trasformare quel file in un punto debole. Se puoi, usa un meccanismo che riduca l’esposizione del segreto e ne semplifichi la rotazione.

Caricamento da variabile d’ambiente

Nel codice, il pattern più comune è leggere il secret da process.env. Ecco un esempio minimale:

const jwt = require('jsonwebtoken'); const secret = process.env.JWT_SECRET; if (!secret) { throw new Error('JWT_SECRET non impostato');
}

Questa verifica iniziale è importante: meglio fallire all’avvio che scoprire il problema solo quando l’app prova a firmare o verificare token in produzione. Se il secret manca, l’applicazione deve dirlo subito e in modo esplicito.

Firma e verifica di un JWT in Node.js

La libreria più diffusa nell’ecosistema Node è jsonwebtoken. Il suo uso è semplice, ma la configurazione va pensata con cura. Il secret serve sia in fase di firma sia in fase di verifica, quindi va mantenuto coerente in tutta l’installazione dell’applicazione.

Esempio di firma:

const jwt = require('jsonwebtoken'); const payload = { sub: 'user-123', role: 'admin'
}; const token = jwt.sign(payload, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: '1h', issuer: 'my-app', audience: 'my-users'
}); console.log(token);

In questo esempio il token contiene solo dati essenziali. Evita di inserire informazioni sensibili nel payload: il JWT non cifra il contenuto, lo firma soltanto. Chiunque intercetti il token può leggerne il contenuto se non è protetto con meccanismi aggiuntivi.

Esempio di verifica:

const decoded = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], issuer: 'my-app', audience: 'my-users'
}); console.log(decoded);

Qui la scelta di specificare algorithms è una buona abitudine: restringe il perimetro e impedisce che l’app accetti algoritmi non previsti. Anche issuer e audience aiutano a limitare l’uso improprio del token in contesti diversi da quelli previsti.

Best practice operative che evitano problemi reali

Il primo errore frequente è usare lo stesso secret per sviluppo, staging e produzione. È comodo, ma sbagliato: se un ambiente meno protetto viene compromesso, il rischio si estende agli altri. Ogni ambiente dovrebbe avere il suo secret, con policy chiare di rotazione e accesso.

Il secondo errore è la mancanza di rotazione. Se un secret resta immutato per anni, basta un incidente per trasformarlo in una chiave compromessa a vita. La rotazione va pianificata prima che serva. Se usi token con durata breve, l’impatto della rotazione è molto più gestibile.

Il terzo errore è ignorare i limiti di lunghezza e formato del secret nelle pipeline. Alcuni sistemi di deploy troncano valori con spazi, newline o caratteri speciali se non gestiti correttamente. Per questo conviene testare il caricamento del secret nell’ambiente reale, non solo in locale.

Controlli minimi da fare all’avvio

Prima di avviare l’app, controlla almeno questi punti:

  • Presenza del secret: process.env.JWT_SECRET non deve essere vuoto.

  • Lunghezza adeguata: il valore deve essere abbastanza lungo da non sembrare una password umana.

  • Algoritmo bloccato: HS256 o l’algoritmo scelto deve essere esplicitamente consentito.

  • Scadenza definita: expiresIn deve essere sempre presente, salvo casi molto particolari.

  • Issuer e audience: utili per limitare l’uso del token fuori contesto.

  • Rotazione del secret senza rompere tutto

    La rotazione è il punto dove molti sistemi si complicano. Se cambi il secret in modo brutale, tutti i token firmati con il vecchio valore diventano invalidi immediatamente. A volte è accettabile, ma spesso significa disconnessioni improvvise e ticket di supporto. Se vuoi evitare effetti collaterali pesanti, devi gestire una finestra di transizione.

    Una strategia semplice è mantenere temporaneamente due chiavi: una attiva per firmare e una o più chiavi vecchie per verificare i token già emessi. Questo approccio è più comune quando si usa una coppia di chiavi asimmetriche, ma il concetto vale anche per il mondo HMAC se la tua architettura supporta più secret in verifica. L’idea è sempre la stessa: firmi con la chiave nuova, accetti ancora per un periodo limitato i token firmati con la vecchia, poi la dismetti.

    Se la tua implementazione non supporta questa logica, allora la rotazione va accompagnata da token a breve scadenza e da un piano di disconnessione controllato. In molti casi è la soluzione più semplice da mantenere.

    Errori da evitare quando crei il secret

    Non usare Math.random() per generarlo. Non è pensato per scopi crittografici. Non usare stringhe statiche copiate da documentazione o esempi, perché finiscono spesso nei log, nei repository o nei template di deploy. Non usare un secret troppo corto solo per comodità visiva. Non condividere lo stesso valore tra più applicazioni senza una ragione architetturale precisa.

    Un altro errore tipico è confondere JWT e cifratura. Un token firmato con HMAC garantisce integrità e autenticità, non riservatezza. Se il payload contiene dati sensibili, il problema non si risolve con il secret. In quel caso bisogna ripensare il contenuto del token o usare un meccanismo diverso.

    Esempio completo con Express

    Un’implementazione minimale con Express può essere strutturata così:

    const express = require('express');
    const jwt = require('jsonwebtoken'); const app = express();
    app.use(express.json()); const secret = process.env.JWT_SECRET;
    if (!secret) { throw new Error('JWT_SECRET non impostato');
    } app.post('/login', (req, res) => { const user = { id: 'user-123', email: 'test@example.com' }; const token = jwt.sign( { sub: user.id, email: user.email }, secret, { algorithm: 'HS256', expiresIn: '1h', issuer: 'my-app', audience: 'my-users' } ); res.json({ token });
    }); app.get('/me', (req, res) => { const auth = req.headers.authorization || ''; const token = auth.startsWith('Bearer ') ? auth.slice(7) : null; if (!token) { return res.status(401).json({ error: 'Token mancante' }); } try { const payload = jwt.verify(token, secret, { algorithms: ['HS256'], issuer: 'my-app', audience: 'my-users' }); return res.json({ user: payload }); } catch (err) { return res.status(401).json({ error: 'Token non valido' }); }
    }); app.listen(3000, () => { console.log('Server in ascolto sulla porta 3000');
    });

    Questo esempio è volutamente essenziale. In un progetto reale aggiungeresti gestione degli errori più strutturata, logging, rate limit, revoca token, refresh token e una strategia chiara per la scadenza. Ma la base resta la stessa: secret robusto, configurazione esterna, algoritmo vincolato, tempi di vita brevi.

    Come verificare che il secret sia gestito bene

    La verifica non è solo funzionale, è anche operativa. Controlla che il valore venga letto dall’ambiente corretto, che non finisca nei log e che il comportamento in assenza del secret sia un errore chiaro. Puoi anche fare un test manuale del token: generarlo, verificarlo, alterarne una parte e confermare che la verifica fallisca come previsto.

    Un test utile è questo: firma un token, modifica una singola lettera della stringa e prova a verificarlo. Il risultato deve essere un errore di firma, non un token accettato. Se non succede, c’è qualcosa di molto sbagliato nella configurazione o nella libreria usata.

    Se usi RS256 invece di HS256

    In alcuni scenari conviene abbandonare il secret simmetrico e passare a una coppia di chiavi asimmetriche. Con RS256 firmi con la private key e verifichi con la public key. Questo separa meglio i ruoli: chi verifica non deve conoscere il segreto di firma. È una scelta spesso più adatta a sistemi distribuiti, microservizi o architetture con più consumatori del token.

    Se il tuo caso d’uso prevede più servizi che devono verificare token ma non firmarli, l’approccio asimmetrico riduce l’esposizione della chiave privata. In compenso introduce gestione delle chiavi più articolata, quindi va adottato solo se il vantaggio operativo è reale.

    Checklist pratica finale

  • Secret generato con un CSPRNG, non a mano.

  • Valore fuori dal repository e fuori dal codice sorgente.

  • Secret diverso per ambiente.

  • Algoritmo esplicitamente vincolato in firma e verifica.

  • Token con scadenza breve e claim di contesto come issuer e audience.

  • Piano di rotazione definito prima dell’incidente.

  • Controllo all’avvio se il secret manca o è vuoto.

  • Se vuoi una regola pratica da portarti dietro, è questa: il JWT è affidabile solo quanto lo è il suo secret. In Node.js la parte difficile non è firmare un token, ma gestire bene il segreto lungo tutto il ciclo di vita dell’applicazione. Ed è lì che si decide se stai costruendo un meccanismo robusto o solo una scorciatoia che prima o poi si rompe.