Garantire la sicurezza Node.js in produzione: guida tecnica

Sicurezza Node.js in produzione: supply chain, OWASP Top 10:2025, Permission Model e checklist operativa.

Dat Giang
CTO of HDWEBSOFT
Diagramma che mostra i livelli di sicurezza Node.js in produzione — supply chain, sandboxing del runtime, autenticazione, protezione dei dati e monitoraggio — attorno al marchio esagonale centrale di Node.js.

Richieste media

HDWEBSOFT accetta richieste dai media

Se sei un giornalista, blogger, influencer o relatore che si occupa di IT e innovazione digitale, i nostri esperti sono disponibili per condividere la loro esperienza diretta e le loro conoscenze per aiutarti a creare contenuti di valore per il tuo pubblico.

Contattaci →

Un’applicazione Node.js in produzione gestisce dati reali degli utenti, secret reali, connessioni di rete reali e centinaia di dipendenze di terze parti. La sicurezza a questa scala non è un componente aggiunto sopra il codice funzionante — è una questione architetturale che modella il modo in cui si selezionano i pacchetti, si configura il runtime, si gestiscono i secret e si risponde agli incidenti.

La sicurezza Node.js in produzione si riferisce all’insieme di pratiche, configurazioni e decisioni architetturali che proteggono un’applicazione Node.js, le sue dipendenze e il suo ambiente runtime da accessi non autorizzati, violazioni dei dati e attacchi alla supply chain lungo l’intero ciclo di vita dello sviluppo software.

Questo articolo è un approfondimento tecnico su come garantire la sicurezza Node.js in produzione, per team di ingegneria che sviluppano o mantengono servizi Node.js in produzione. Per una panoramica di base sulle vulnerabilità comuni di Node.js e sulle pratiche di mitigazione di base, si veda la nostra guida complementare sulle best practice di sicurezza Node.js. Qui ci concentriamo sulle aree che differenziano un programma di sicurezza di livello production: integrità della supply chain, mappatura OWASP Top 10:2025, il Node.js Permission Model e una checklist di sicurezza implementabile.

Perché la sicurezza Node.js richiede un approccio di livello production

In sviluppo, una dipendenza vulnerabile è un avviso in un terminale. In produzione, è una superficie di attacco che un avversario può sondare continuamente. L’ecosistema npm rende possibile assemblare un backend ricco di funzionalità in pochi giorni, ma ogni pacchetto che si installa diventa parte del proprio perimetro di sicurezza. Un singolo pacchetto compromesso — che sia per account takeover, typosquatting o un maintainer malevolo — può iniettare codice nella pipeline di build, nel runtime e nelle sessioni degli utenti.

La compromissione del settembre 2025 della famiglia di pacchetti npm chalk e debug ha illustrato quanto rapidamente accada. Quelle librerie registrano oltre 2,6 miliardi di download settimanali combinati e si trovano in profondità negli alberi delle dipendenze che la maggior parte degli ingegneri non ispeziona mai direttamente. Versioni malevole sono rimaste attive per circa due ore prima che il maintainer le rilevasse e rimuovesse — ma ogni pipeline CI, build server o macchina di sviluppo che ha eseguito npm install in quella finestra ha scaricato il codice compromesso.

Il contesto di produzione cambia ciò che è in gioco. Si gestiscono secret reali, dati utente reali, obblighi di compliance reali e traffico reale. Una vulnerabilità teorica in un progetto collaterale diventa un incidente segnalabile in produzione. Questo divario è il motivo per cui un programma di sicurezza Node.js di livello production non può fermarsi a “eseguire npm audit e correggere ciò che segnala”. Deve affrontare l’intero ciclo di vita: come i pacchetti entrano nel progetto, come il runtime è blindato, come autenticazione e dati sono protetti e come si rileva e si risponde quando qualcosa va storto.

Mappatura OWASP Top 10:2025 a Node.js

L’OWASP Top 10:2025 è l’ultima edizione del documento di consapevolezza standard del settore per i rischi di sicurezza delle applicazioni web. La release 2025 ha ristrutturato la lista per riflettere i pattern di attacco moderni, elevando in particolare Software Supply Chain Failures ad A03 e introducendo Mishandling of Exceptional Conditions come A10. Mappare queste categorie ai rischi specifici di Node.js offre ai team di ingegneria un punto di partenza concreto per la prioritarizzazione.

OWASP 2025Rischio specifico Node.jsMitigazione
A01 Broken Access ControlJWT token sovraprivilegiati, middleware RBAC mancante, IDOR nelle route APIScope dei token con least-privilege, middleware RBAC, verifica della proprietà delle risorse
A02 Security MisconfigurationDebug mode in produzione, CORS aperto, impostazioni helmet predefinite, risposte di errore verboseConfigurazione basata su ambiente, origin CORS bloccate, helmet con CSP personalizzato, output di errore sanitarizzato
A03 Software Supply Chain FailuresExploit delle dipendenze npm, typosquatting, account takeover del maintainer, propagazione transitiva di vulnerabilitàLockfile, verifica della provenienza npm, npm audit signatures, generazione di SBOM, registry pinning
A04 Cryptographic FailuresHash delle password debole, secret hardcoded, TLS obsoleto, crittografia at rest mancanteArgon2id o bcrypt, KMS/Vault per i secret, versione TLS minima approvata, crittografia a livello di campo per i PII
A05 InjectionSQL/NoSQL injection tramite input non sanitarizzato, command injection in child_process, XSS nell’output renderizzatoValidazione dello schema Joi/Zod, query parametrizzate (Prisma, Drizzle), express-mongo-sanitize, encoding dell’output
A06 Insecure DesignNessun threat modeling, controlli di sicurezza mancanti integrati nell’architettura, integrazioni di terze parti considerate sicurate per assuntoThreat modeling STRIDE in fase di design, security review per nuove funzionalità, pattern di integrazione zero-trust
A07 Authentication FailuresJWT di lunga durata senza rotazione, gestione debole delle sessioni, MFA mancante, token prevedibiliScadenza breve dei JWT con rotazione dei refresh token, flag dei cookie sicuri, applicazione MFA, invalidazione delle sessioni
A08 Software or Data Integrity FailuresPacchetti npm non firmati, artefatti CI manomessi, nessuna attestazione di buildProvenienza Sigstore, firma della pipeline CI, controlli di integrità sugli artefatti distribuiti
A09 Security Logging and Alerting FailuresNessun log strutturato, mancato rilevamento degli eventi di sicurezza, nessun alerting sulle anomalieLogging strutturato con pino/winston, schema degli eventi di sicurezza, alerting sui fallimenti di autenticazione e sui trigger di rate-limit
A10 Mishandling of Exceptional ConditionsErrori inghiottiti che rivelano stack trace, promise rejection non gestite che bloccano il processo, divulgazione di informazioni nelle risposte di erroreMiddleware centralizzato di gestione degli errori, nessuno stack trace nelle risposte di produzione, graceful shutdown sulle rejection non gestite

Questa mappatura non è una checklist da completare una volta. È una lente per rivedere ogni nuova funzionalità, dipendenza e modifica di configurazione rispetto alle categorie che contano di più in un contesto Node.js.

Uno scudo diviso in dieci segmenti che rappresentano le categorie OWASP Top 10:2025, con il marchio esagonale di Node.js al centro, a illustrare la copertura di sicurezza completa.

Proteggere la supply chain di Node.js

La sicurezza della supply chain è l’area in cui la maggior parte delle applicazioni Node.js in produzione è più debole, ed è dove questa guida differisce maggiormente da una panoramica di sicurezza di base. Il registry npm è il più grande ecosistema di pacchetti open source, e il suo basso attrito di pubblicazione lo rende un bersaglio attraente per gli attaccanti. Gli account takeover, il typosquatting e le pipeline automatizzate di generazione di pacchetti sono tutti aumentati drasticamente, e i pacchetti presi di mira non sono oscuri — sono profondamente integrati negli alberi delle dipendenze di produzione.

I lockfile sono la prima linea di difesa

Un lockfile committato (package-lock.json o pnpm-lock.yaml) fissa ogni dipendenza a una versione specifica e a un hash di integrità. Senza un lockfile, npm install risolve l’ultima versione che soddisfa i range del package.json, il che significa che una pubblicazione malevola o accidentalmente difettosa può atterrare nella build senza alcuna modifica del codice da parte propria.

Committare i lockfile nel version control. Rivedere i diff dei lockfile nelle pull request come si rivede il codice — una nuova dipendenza transitiva che compare in un aggiornamento minor è un segnale da investigare.

Verificare l’integrità dei pacchetti con npm audit signatures

npm audit identifica le vulnerabilità note nell’albero delle dipendenze, ma non verifica che i pacchetti scaricati siano ciò che l’autore intendeva pubblicare. È qui che entra in gioco npm audit signatures. Verifica le firme del registry e le attestazioni di provenienza sui pacchetti installati, dando la fiducia che il tarball scaricato corrisponda a ciò che è stato pubblicato e, dove disponibile, che sia stato costruito da uno specifico workflow CI a partire da uno specifico commit.

# Verify registry signatures and provenance attestations
npm audit signatures

Eseguire questo in CI come passo di gate. Se un pacchetto fallisce la verifica della firma, la build non deve procedere.

Preferire i pacchetti con provenienza Sigstore

La provenienza npm collega un pacchetto pubblicato alla specifica esecuzione di GitHub Actions o GitLab CI che lo ha costruito, firmata tramite Sigstore e registrata in un transparency ledger pubblico. Quando si ha la scelta tra due pacchetti di qualità simile, preferire quello che pubblica con --provenance. Non garantisce che il pacchetto sia privo di codice malevolo, ma offre un collegamento verificabile tra il repository sorgente, il processo di build e l’artefatto che si installa.

Generare un Software Bill of Materials

Un SBOM è un inventario leggibile da macchina di ogni componente dell’applicazione, incluse le dipendenze transitive. Strumenti come npm sbom (disponibile in npm 9+) o cyclonedx-npm generano SBOM in formati standard come SPDX o CycloneDX. Un SBOM permette di rispondere rapidamente a “siamo interessati da questa CVE?” senza tracciare manualmente gli alberi delle dipendenze, ed è sempre più richiesto dagli appalti enterprise e dai framework di compliance.

# Generate a CycloneDX SBOM
npx @cyclonedx/cyclonedx-npm --output-file sbom.json

Fissare il registry e controllare gli script di installazione

Fissare l’URL del registry in .npmrc per prevenire la risoluzione accidentale da un mirror non affidabile:

# .npmrc
registry=https://registry.npmjs.org/
audit-level=high

Gli script di installazione possono eseguire codice arbitrario durante npm install. Impostare ignore-scripts=true in .npmrc li disabilita, ma alcuni pacchetti richiedono script di installazione per funzionare — ad esempio, pacchetti che compilano native addon o scaricano binari specifici della piattaforma. Utilizzare ignore-scripts=true quando le dipendenze e il flusso di lavoro lo consentono e verificare prima di applicarlo globalmente. Se una dipendenza critica si rompe con gli script disabilitati, valutare se quella dipendenza merita il rischio.

Diagramma dei livelli di sicurezza della supply chain npm — un pacchetto passa attraverso la verifica del lockfile, i controlli delle firme e la generazione dell'SBOM prima di raggiungere l'applicazione.

Node.js Permission Model — Sandboxing a livello di processo

Il Node.js Permission Model è un meccanismo integrato per limitare le risorse di sistema a cui un processo Node.js può accedere. È diventato stabile in Node.js 22.13.0 e non è più sperimentale.

Quando si avvia Node.js con il flag --permission, al processo viene negato l’accesso al filesystem, ai processi figli, ai worker thread, ai native addon, a WASI e al runtime inspector per impostazione predefinita. Si concede quindi selettivamente l’accesso tramite i flag --allow-*:

# Allow reading from /app and /tmp, writing to /app/logs only
node --permission \
  --allow-fs-read=/app,/tmp \
  --allow-fs-write=/app/logs \
  server.js
# Allow child processes but not worker threads or native addons
node --permission --allow-child-process server.js

Cosa è il Permission Model — e cosa non è

Il Permission Model implementa un approccio a cintura di sicurezza. Impedisce al codice affidabile di modificare involontariamente file o di utilizzare risorse a cui non è stato esplicitamente concesso l’accesso. Ciò è utile per limitare il blast radius: se una dipendenza ha un bug che tenta di scrivere su /etc/passwd, il Permission Model lo blocca.

Tuttavia, il Permission Model non è un sandbox di sicurezza completo. Secondo la Node.js Security Policy ufficiale, Node.js considera affidabile qualsiasi codice che gli venga chiesto di eseguire. Il codice malevolo può aggirare il permission model ed eseguire codice arbitrario senza le restrizioni da esso imposte. Il Permission Model non fornisce garanzie di sicurezza in presenza di codice malevolo o non affidabile.

Utilizzare il Permission Model come uno strato in una strategia di defense-in-depth — insieme alla sicurezza dei container, all’esecuzione non-root e ai controlli di rete — non come sostituto di nessuno di questi.

AspettoPermission ModelSicurezza dei container
AmbitoRisorse del processo Node.js (fs, child_process, worker, addon)Isolamento a livello di OS (filesystem, rete, utenti, capability)
Modello di minacciaCodice affidabile incidenteProcesso non affidabile o compromesso
Resistenza al bypassBassa — il codice malevolo può aggirarePiù alta — isolamento enforced dal kernel
Complementare?Sì — utilizzare entrambiSì — utilizzare entrambi

Un processo Node.js racchiuso in un confine di permessi semi-trasparente, con le risorse consentite che passano attraverso punti di accesso controllati e le risorse negate bloccate, a illustrare l'approccio a cintura di sicurezza del Permission Model.

Rafforzamento di autenticazione e autorizzazione

L’autenticazione e l’autorizzazione sono l’origine della maggior parte degli incidenti in produzione. Una policy dei token debole o un controllo di accesso mancante possono esporre i dati di ogni utente in una singola richiesta.

JWT: scadenza breve con rotazione dei refresh token

I JWT di lunga durata sono una responsabilità. Se un token viene rubato, rimane valido fino alla scadenza. Utilizzare una scadenza breve dell’access token (15 minuti o meno) e ruotare i refresh token a ogni utilizzo. Quando un refresh token viene utilizzato per emettere un nuovo access token, emettere un nuovo refresh token e invalidare il precedente.

// Issue a short-lived access token
const accessToken = jwt.sign(
  { sub: userId, scope: 'user' },
  process.env.JWT_SECRET,
  { expiresIn: '15m', algorithm: 'HS256' }
);

// Rotate refresh token on each use
async function refresh(refreshToken) {
  const payload = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
  const stored = await getRefreshToken(payload.jti);
  if (!stored || stored.revoked) throw new Error('Invalid refresh token');
  await revokeRefreshToken(payload.jti);
  return issueTokenPair(payload.sub);
}

Hash delle password: Argon2id preferito, bcrypt come fallback

Preferire Argon2id quando lo stack lo supporta. Argon2id è l’algoritmo di hashing delle password raccomandato nell’OWASP Password Storage Cheat Sheet perché è resistente sia agli attacchi GPU che a quelli side-channel. bcrypt rimane un’opzione matura e ampiamente compatibile quando Argon2id non è disponibile o quando serve compatibilità con database di password esistenti.

// Argon2id (preferred)
const argon2 = require('argon2');
const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 65536,
  timeCost: 3,
  parallelism: 4,
});

// bcrypt (mature fallback)
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash(password, 12);

Non utilizzare mai MD5, SHA1 o testo in chiaro per la memorizzazione delle password.

Rate limiting e protezione brute-force

Applicare il rate limiting agli endpoint di autenticazione in modo specifico, non solo globalmente. Sia express-rate-limit che @fastify/rate-limit supportano la configurazione per singola route:

const rateLimit = require('express-rate-limit');

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  standardHeaders: true,
  message: 'Too many login attempts, try again later.',
});

app.post('/auth/login', authLimiter, loginHandler);

Autenticazione service-to-service nei sistemi distribuiti

Quando i servizi Node.js comunicano tra loro, il mutual TLS o i service token firmati offrono garanzie più forti rispetto alle API key condivise. Per un approfondimento sui pattern di autenticazione e sicurezza nelle architetture Node.js distribuite, si veda la nostra guida sui microservizi Node.js.

Sicurezza dei dati in transito e at rest

Configurazione TLS

Preferire TLS 1.3 e mantenere una versione TLS minima approvata in base ai requisiti di compatibilità. Applicare solo TLS 1.3 potrebbe rompere client o integrazioni che richiedono ancora TLS 1.2. Documentare la versione TLS minima e rivederla quando si aggiorna il runtime o l’infrastruttura.

Gestione dei secret in produzione

Non affidarsi ai file .env per la gestione dei secret in produzione. I file .env sono appropriati per lo sviluppo e il testing locale, ma i secret di produzione dovrebbero essere gestiti tramite un secret store dedicato — AWS Secrets Manager, Google Secret Manager, HashiCorp Vault o il secret store integrato della propria piattaforma. Questi forniscono rotazione, audit logging e controllo degli accessi che i file .env non possono offrire.

// Load secrets from AWS Secrets Manager in production
const { SecretsManagerClient, GetSecretValueCommand } =
  require('@aws-sdk/client-secrets-manager');

const client = new SecretsManagerClient({ region: process.env.AWS_REGION });

async function getSecret(name) {
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: name })
  );
  return JSON.parse(response.SecretString);
}

Crittografia a livello di campo per i dati sensibili

Per i PII o i dati regolamentati, considerare la crittografia a livello di campo prima di memorizzare i record. Ciò limita l’esposizione in caso di compromissione del database — un attaccante con accesso in lettura al database vede ciphertext, non plaintext.

Per una prospettiva più ampia sul perché la gestione della sicurezza dei dati conti a livello organizzativo, si veda la nostra analisi sulla gestione della sicurezza dei dati.

Validazione degli input e difesa dalle injection

La validazione degli input è la difesa più affidabile contro gli attacchi di injection. Validare ogni input rispetto a uno schema esplicito prima che raggiunga la business logic.

Validazione dello schema con Joi o Zod

// Joi
const Joi = require('joi');

const userSchema = Joi.object({
  email: Joi.string().email().required(),
  password: Joi.string().min(12).max(128).required(),
  role: Joi.string().valid('admin', 'user').default('user'),
});

const { value, error } = userSchema.validate(req.body);
if (error) return res.status(400).json({ error: error.message });
// Zod
const { z } = require('zod');

const userSchema = z.object({
  email: z.string().email(),
  password: z.string().min(12).max(128),
  role: z.enum(['admin', 'user']).default('user'),
});

const parsed = userSchema.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error });

NoSQL injection e query parametrizzate

Se si utilizza MongoDB, sanitarizzare gli input per prevenire l’operator injection con express-mongo-sanitize. Per i database SQL, utilizzare query parametrizzate tramite un ORM o un query builder — Prisma, Drizzle e Knex parametrizzano tutti per impostazione predefinita. Non concatenare mai l’input dell’utente in una stringa di query.

Header di sicurezza e configurazione del server

helmet con CSP personalizzato

helmet imposta valori predefiniti sensati, ma per la produzione si dovrebbe personalizzare il Content-Security-Policy in base alle origini effettive delle risorse dell’applicazione. Un CSP basato su nonce è più forte di uno statico perché previene l’iniezione di script inline anche se un attaccante trova un vettore XSS.

const helmet = require('helmet');
const crypto = require('crypto');

app.use((req, res, next) => {
  res.locals.cspNonce = crypto.randomBytes(16).toString('base64');
  next();
});

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", 'data:', 'https:'],
        connectSrc: ["'self'"],
        frameAncestors: ["'none'"],
        objectSrc: ["'none'"],
      },
    },
    hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
  })
);

CORS bloccato su origin specifiche

Non utilizzare mai cors({ origin: '*' }) in produzione. Elencare le origin esatte che sono autorizzate:

const cors = require('cors');

const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];

app.use(
  cors({
    origin: (origin, callback) => {
      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
    credentials: true,
  })
);

Sicurezza dei container e del deployment per Node.js

Eseguire come utente non-root

FROM node:22-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

# Create and switch to a non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

# Optional: apply Permission Model flags at runtime
CMD ["node", "--permission", "--allow-fs-read=/app", "--allow-fs-write=/app/logs", "server.js"]

Fissare le immagini base affidabili e scansionare le vulnerabilità

Non utilizzare latest come tag dell’immagine base. Fissare a un digest specifico in modo che un aggiornamento dell’immagine base non modifichi silenziosamente la build:

FROM node:22-slim@sha256:<specific-digest>

Le immagini distroless e slim riducono la superficie di attacco rimuovendo strumenti shell e package manager, ma non sono automaticamente sicure. Scansionare ogni immagine — distroless o meno — con Trivy, Grype o lo scanner integrato del proprio registry. Un’immagine slim con una dipendenza vulnerabile è comunque un’immagine vulnerabile.

Filesystem in sola lettura

Eseguire il container con un root filesystem in sola lettura e montare volumi scrivibili solo dove l’applicazione ne ha bisogno:

docker run --read-only --tmpfs /tmp -v ./logs:/app/logs node-app

Health check e graceful shutdown

Gli health check e il graceful shutdown sono principalmente questioni di affidabilità, ma prevengono anche stati di parziale failure che possono esporre problemi di sicurezza. Assicurarsi che l’applicazione chiuda le connessioni al database e smetta di accettare nuove richieste prima di uscire.

Per considerazioni più ampie sulla sicurezza del deployment cloud, si veda la nostra guida sulla sicurezza del deployment cloud.

Monitoraggio, logging e risposta agli incidenti

Logging strutturato con schema degli eventi di sicurezza

Utilizzare pino o winston con uno schema JSON coerente per gli eventi di sicurezza. Ogni tentativo di autenticazione, decisione di controllo degli accessi, trigger di rate-limit ed errore dovrebbero essere registrati con contesto sufficiente per ricostruire cosa è successo:

const pino = require('pino');
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });

// Log a security event
logger.info({
  event: 'auth.login',
  outcome: 'failure',
  userId: req.body.email,
  ip: req.ip,
  reason: 'invalid_credentials',
}, 'Login attempt failed');

Alerting sulle anomalie

Inoltrare i log a un sistema centrale — Datadog, New Relic, CloudWatch o uno stack ELK — e configurare alert per:

  • Fallimenti ripetuti di autenticazione da un singolo IP
  • Superamento delle soglie di rate-limit su endpoint sensibili
  • Connessioni di rete in uscita inattese
  • Picchi nel tasso di errore
  • Fallimenti della verifica dell’integrità dei pacchetti in CI

Runbook di risposta agli incidenti

Mantenere un runbook che copra: chi viene notificato, come revocare credenziali e token, come eseguire il rollback di un deployment, come isolare un servizio compromesso e come comunicare con gli utenti interessati. Testare periodicamente il runbook — un piano non testato è un piano che fallisce sotto pressione.

Checklist di sicurezza Node.js per la produzione

Una lavagna di checklist di sicurezza per la produzione con sette sezioni raggruppate — supply chain, runtime, autenticazione, dati, input, header e monitoraggio — ciascuna contrassegnata con un segno di spunta, a rappresentare un programma di sicurezza Node.js completo.

  1. Supply chain

    • Committare e rivedere i lockfile in ogni pull request
    • Eseguire npm audit signatures in CI come passo di gate
    • Preferire pacchetti con attestazioni di provenienza Sigstore
    • Generare e memorizzare un SBOM per ogni release
    • Fissare l’URL del registry in .npmrc; utilizzare ignore-scripts dove le dipendenze lo consentono
  2. Runtime

    • Eseguire Node.js come utente non-root
    • Applicare i flag del Permission Model (--permission, --allow-fs-*) dove fattibile
    • Fissare l’immagine base a un digest specifico; scansionare ogni immagine
    • Utilizzare un filesystem del container in sola lettura con mount scrivibili espliciti
  3. Autenticazione

    • Scadenza breve dei JWT (15 min) con rotazione dei refresh token
    • Argon2id per l’hash delle password (bcrypt come fallback maturo)
    • MFA sugli endpoint di amministrazione e ad alto privilegio
    • Flag dei cookie sicuri: httpOnly, secure, sameSite
    • Rate limiting sugli endpoint di autenticazione
  4. Dati

    • Versione TLS minima approvata in base alle esigenze di compatibilità
    • Secret di produzione in KMS, Vault o nel secret store della piattaforma — non in .env
    • Crittografia a livello di campo per PII o dati regolamentati
  5. Input

    • Validazione dello schema Joi o Zod su ogni corpo di richiesta
    • Query parametrizzate (Prisma, Drizzle, Knex)
    • express-mongo-sanitize per MongoDB
    • Encoding dell’output per i contenuti renderizzati
  6. Header

    • helmet con CSP personalizzato basato su nonce
    • HSTS con includeSubDomains e preload
    • CORS bloccato su origin specifiche
  7. Monitoraggio

    • Log JSON strutturati con schema degli eventi di sicurezza
    • Alert per fallimenti di autenticazione, hit di rate-limit e fallimenti di integrità
    • Runbook di risposta agli incidenti testato

Domande frequenti

Come si garantisce la sicurezza Node.js in produzione?

È necessario un approccio multistrato: integrità della supply chain attraverso lockfile, npm provenance e audit signatures; sandboxing del runtime tramite il Permission Model ed esecuzione non-root; autenticazione robusta con rotazione JWT e Argon2id; validazione degli input con Joi o Zod; header di sicurezza con helmet; e monitoraggio continuo con pianificazione della risposta agli incidenti.

Cos’è il Node.js Permission Model?

Il Node.js Permission Model è una funzionalità stabile a partire da Node 22.13.0 che limita l’accesso del processo al filesystem, ai processi figli, ai worker e ai native addon tramite i flag --permission e --allow-*. È una cintura di sicurezza per codice affidabile, non un sandbox di sicurezza completo contro codice malevolo.

Come si proteggono le dipendenze npm dagli attacchi alla supply chain?

Utilizzare lockfile, eseguire npm audit signatures per verificare la provenienza, preferire pacchetti con attestazioni di provenienza Sigstore, generare SBOM, fissare gli URL del registry in .npmrc e disabilitare condizionalmente gli script di installazione con ignore-scripts quando le dipendenze e il flusso di lavoro lo consentono.

Quali sono le vulnerabilità di sicurezza Node.js più comuni?

Mappate su OWASP Top 10:2025, le più comuni includono broken access control, security misconfiguration, software supply chain failures, cryptographic failures, injection e authentication failures — con vettori specifici di Node.js come exploit delle dipendenze npm e blocco dell’event loop.

npm audit è sufficiente per proteggere le dipendenze Node.js?

No. npm audit identifica le vulnerabilità note nelle dipendenze ma non verifica l’integrità o la provenienza dei pacchetti. Utilizzare npm audit signatures per la verifica di firme e provenienza, committare i lockfile, preferire pacchetti con attestazioni Sigstore e generare SBOM per la piena visibilità della supply chain.

Il Node.js Permission Model è un sandbox di sicurezza?

No. Il Permission Model è una cintura di sicurezza che impedisce al codice affidabile di accedere involontariamente a risorse al di fuori del proprio ambito. Non fornisce garanzie di sicurezza contro codice malevolo, che può aggirare il modello. Utilizzarlo insieme alla sicurezza dei container, non come sostituto.

Conclusione

La sicurezza Node.js di livello production non è un singolo strumento o un audit una tantum. È un programma che abbraccia l’integrità della supply chain, il rafforzamento del runtime, l’autenticazione, la protezione dei dati e il monitoraggio continuo. L’OWASP Top 10:2025 offre un framework per la prioritarizzazione, il Permission Model offre un nuovo strato di controllo del runtime e le pratiche di supply chain come la verifica della provenienza e la generazione di SBOM colmano il divario che npm audit da solo non può.

Se il suo team sta sviluppando o scalando un’applicazione Node.js e ha bisogno di un partner che tratti la sicurezza come una questione architetturale piuttosto che come un ripensamento, HDWEBSOFT porta oltre un decennio di esperienza nei servizi di sviluppo Node.js. Ci contatti per discutere come possiamo aiutarla a rilasciare applicazioni Node.js sicure, resilienti e pronte per la produzione.

Dat Giang

Dat Giang

CTO of HDWEBSOFT

Experienced developer passionate about delivering practical, innovative outsourcing software development solutions with integrity.

contact@hdwebsoft.com +84 (0)28 66809403 15 Thep Moi, Bay Hien Ward, Ho Chi Minh City, Vietnam