06 - Supply Chain Security: npm audit, SBOM e Dependency Management
Il settembre 2025 ha segnato un punto di svolta nella storia della sicurezza software: diciotto pacchetti npm tra i più scaricati al mondo, tra cui chalk, debug e ansi-styles, vengono compromessi attraverso una campagna di phishing mirata ai maintainer. In poche ore, oltre 2,6 miliardi di download settimanali vengono esposti a codice JavaScript obfuscato progettato per intercettare transazioni in criptovaluta. Non si tratta di un attacco alla tua applicazione, ma all'ecosistema che la sostiene.
Questo scenario illustra la natura fondamentale degli attacchi alla supply chain: colpiscono non
il codice che scrivi, ma il codice di cui ti fidi. Ogni dipendenza che installi con
npm install rappresenta codice scritto da altri, mantenuto da altri, e potenzialmente
compromesso da altri. In un progetto Node.js di media dimensione, le dipendenze dirette sono
tipicamente 20-50 pacchetti, ma il grafo completo delle dipendenze transitive può raggiungere
facilmente le 500-1000 librerie. Stai davvero controllando tutto questo codice?
Secondo OWASP Top 10:2025, la categoria A03: Software and Data Integrity Failures include esplicitamente le supply chain failures come vettore critico. Questa e una delle due nuove categorie introdotte nell'edizione 2025, un riconoscimento esplicito che la sicurezza del codice non può più prescindere dalla sicurezza delle dipendenze. Questo articolo ti fornisce gli strumenti concreti per proteggerti.
Cosa Imparerai
- Anatomia di un supply chain attack: typosquatting, dependency confusion, lockfile poisoning
- npm audit, yarn audit, pnpm audit: utilizzo avanzato e automazione
- Integrita dei lockfile e verifica degli hash con npm ci
- SBOM: generazione con CycloneDX e SPDX, standard NTIA
- Snyk e Dependabot: monitoraggio continuo delle vulnerabilità
- GitHub Actions hardening: SHA-pinning e permissions minime
- Container image security: Trivy, Syft e firma con Cosign/Sigstore
- Dependency confusion e come difendersi con npm scopes privati
Anatomia di un Supply Chain Attack
Capire come funzionano gli attacchi alla supply chain e il primo passo per difendersi. Esistono diverse categorie principali, ognuna con caratteristiche e vettori di attacco distinti.
Typosquatting: il pericolo di un typo
Il typosquatting sfrutta errori di digitazione comuni. Un attaccante pubblica
lodahs (invece di lodash), requst (invece di
request), o colerrs (invece di colors). Nel 2025,
ricercatori di sicurezza hanno individuato un set di pacchetti typosquattati progettati per
mimare librerie popolari che lanciavano terminali nascosti tramite script postinstall
per esfiltrare credenziali in modo silenzioso.
La difesa principale e la verifica accurata del nome prima di installare qualsiasi pacchetto. Strumenti come Snyk e Socket.dev analizzano automaticamente la somiglianza dei nomi con pacchetti esistenti e segnalano potenziali typosquatting prima dell'installazione.
Dependency Confusion: lo scope pubblico vs privato
La dependency confusion (o namespace confusion) e un attacco più sofisticato documentato per la prima volta da Alex Birsan nel 2021. L'attaccante pubblica su npm pubblico un pacchetto con lo stesso nome di un pacchetto interno privato dell'azienda target, ma con un numero di versione più alto. npm, per default, risolve la versione più recente dal registro pubblico, portando il pacchetto malevolo a sostituire quello legittimo.
Caso Reale: Dependency Confusion su Larga Scala
Nel 2021, Alex Birsan ha compromesso oltre 35 grandi aziende tra cui Microsoft, Apple, PayPal e Shopify usando questa tecnica, guadagnando più di 130.000 dollari in bug bounty. Il meccanismo era semplice: trovare nomi di pacchetti interni nei file di configurazione pubblici (package.json, pyproject.toml) e pubblicarli con versione 9.9.9 su registri pubblici.
Maintainer Account Takeover
L'attacco più insidioso del 2025 ha dimostrato come il phishing mirato ai maintainer sia diventato il vettore preferito. Una volta compromesso l'account di un maintainer legittimo, l'attaccante pubblica versioni malevole di pacchetti già affidabili. La community si fida del package, i test di sicurezza passano, e il codice malevolo entra in produzione.
Lockfile Poisoning
In un attacco di lockfile poisoning, un contributore malevolo modifica direttamente il
package-lock.json o yarn.lock in una pull request per puntare a
versioni compromesse o hash diversi da quelli attesi. Se il processo di review non include
la verifica del lockfile, il codice malevolo passa inosservato.
npm audit: Utilizzo Avanzato
npm audit e il punto di partenza, ma usarlo correttamente richiede più di
una semplice esecuzione. Vediamo come integrarlo in modo efficace nel workflow di sviluppo.
# Audit base con output JSON per processing automatico
npm audit --json
# Audit solo production dependencies (esclude devDependencies)
npm audit --omit=dev
# Fissa automaticamente le vulnerabilità patchabili
npm audit fix
# Fix anche di breaking changes (usare con cautela)
npm audit fix --force
# Audit con soglia di severita: exit code 1 se ci sono critical
npm audit --audit-level=critical
# Audit con soglia moderate
npm audit --audit-level=moderate
# Output in formato per CI/CD
npm audit --json | jq '.metadata.vulnerabilities'
Per un progetto con molte dipendenze, il numero di vulnerabilità può essere elevato e
difficile da gestire. Una strategia efficace e configurare un file .npmrc
con le policy di audit del progetto, oppure usare npm audit con un file
di configurazione di eccezioni.
# .nsprc (Node Security Project configuration)
# Oppure usa audit-resolve.json con npm-audit-resolver
# Installazione di npm-audit-resolver per gestire eccezioni
npm install -g npm-audit-resolver
# Processo interattivo per gestire ogni vulnerabilità
audit-resolve
# Verifica successiva (usa le eccezioni salvate)
audit-resolve --ci
# Script package.json per CI sicuro
# package.json
{
"scripts": {
"audit:ci": "npm audit --audit-level=high --omit=dev",
"audit:full": "npm audit --json > audit-report.json",
"audit:check": "npm audit --audit-level=critical"
}
}
pnpm audit e yarn audit
Se usi pnpm o yarn, i comandi di audit sono simili ma con alcune differenze importanti. pnpm offre un controllo più granulare sulle dipendenze, mentre yarn v2/v3 (Berry) ha migliorato significativamente la gestione degli hash.
# pnpm audit
pnpm audit
pnpm audit --audit-level high
pnpm audit --prod # solo production
# yarn audit (classic v1)
yarn audit
yarn audit --level high
# yarn audit (Berry v2/v3)
yarn npm audit
yarn npm audit --severity high
# Output JSON per processing
pnpm audit --json | jq '.advisories | length'
Integrita dei Lockfile: la Prima Linea di Difesa
Il lockfile (package-lock.json, yarn.lock, pnpm-lock.yaml)
e fondamentale per la riproducibilità delle build e la sicurezza. Ogni entry nel lockfile
include non solo la versione esatta, ma anche l'hash del contenuto del pacchetto.
npm ci vs npm install: una Distinzione Critica
In produzione e CI/CD, usa sempre npm ci invece di npm install.
npm ci installa esattamente le versioni specificate nel lockfile, verifica
gli hash crittografici di ogni pacchetto, e fallisce se il lockfile e out-of-sync con
il package.json. npm install può aggiornare il lockfile silenziosamente.
# CORRETTO per CI/CD: verifica integrita lockfile
npm ci
# Verifica manuale degli hash nel lockfile
# package-lock.json contiene entries come:
# "node_modules/lodash": {
# "version": "4.17.21",
# "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
# "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZboqV76wE2wDvQ6",
# }
# Verifica che il lockfile non sia stato modificato
git diff package-lock.json | head -50
# Pre-commit hook per prevenire modifiche non autorizzate al lockfile
# .husky/pre-commit
#!/bin/sh
if git diff --cached --name-only | grep -q "package-lock.json"; then
echo "WARNING: package-lock.json modificato. Verifica le dipendenze."
npm audit --audit-level=high
fi
Configurazione .npmrc per Sicurezza
Il file .npmrc permette di configurare npm con policy di sicurezza che si
applicano a tutto il progetto o all'utente corrente.
# .npmrc - configurazione sicurezza progetto
# Richiedi sempre HTTPS per il registro
registry=https://registry.npmjs.org/
# Abilita strict-ssl (default: true, non disabilitare mai!)
strict-ssl=true
# Audit automatico dopo ogni install
audit=true
# Fund messages: disabilita per CI
fund=false
# Usa lockfile (default: true)
package-lock=true
# Per workspace con pacchetti privati su registro Artifactory/Nexus
# @mycompany:registry=https://npm.mycompany.internal/
# //npm.mycompany.internal/:_authToken={NPM_TOKEN}
# Prevenzione dependency confusion: scope sempre sul registro privato
# @internal:registry=https://npm.internal.company.com/
Dependency Confusion: Difendersi con Scopes Privati
La difesa più efficace contro la dependency confusion e assicurarsi che i pacchetti privati usino sempre uno scope dedicato e che npm sia configurato per risolvere quello scope esclusivamente dal registro interno.
# package.json con scope privato corretto
{
"dependencies": {
"@mycompany/auth-utils": "^2.1.0",
"@mycompany/api-client": "^1.5.0"
}
}
# .npmrc - scope privati sempre sul registro interno
@mycompany:registry=https://npm.mycompany.internal/
//npm.mycompany.internal/:_authToken=






