Codice sicuro
Codice pulito e codice sicuro sono la stessa cosa.
Una vulnerabilità è quasi sempre un bug normale finito nel posto sbagliato. Ecco perché la chiarezza conta, quali sono gli errori più comuni e cosa controllare prima di ogni rilascio.
Perché il codice pulito è più sicuro
Il codice che si capisce al primo colpo si rivede meglio, si testa meglio e nasconde meno sorprese. La sicurezza non è uno strato da aggiungere alla fine: nasce dalle stesse abitudini che rendono il codice leggibile.
-
Nomi che dicono la verità
Quando un valore arriva dall’esterno, il nome deve ricordarlo:
rawEmailountrustedHtmlti avvisano,datano. -
Una funzione, un compito
Una funzione che valida e basta si testa in un minuto. Una da duecento righe che valida, salva e manda email nasconde i controlli saltati.
-
Errori gestiti, non ignorati
Un
catchvuoto è un allarme spento. Registra l’errore senza dati sensibili, mostra all’utente un messaggio generico e non proseguire come se nulla fosse.
Un esempio: la stessa query, scritta in due modi
Vulnerabile SQL injection
// l'input dell'utente diventa parte della query
const rows = await db.query(
`SELECT * FROM users
WHERE email = '${req.body.email}'`
);
Sicuro query parametrizzata
// query e dati viaggiano separati
const email = String(req.body.email ?? '').trim();
const rows = await db.query(
'SELECT id, name FROM users WHERE email = $1',
[email]
);
Se qualcuno scrive ' OR '1'='1 nel campo email, la prima versione restituisce tutti gli utenti del
database. La seconda cerca semplicemente un’email strana e non trova niente. In più chiede solo le colonne che
servono: se qualcosa va storto, esce meno.
Le 10 vulnerabilità più diffuse
L’OWASP Top 10 è la classifica di riferimento dei rischi per le applicazioni web, curata dalla comunità OWASP. L’edizione più recente è la 2025: la catena di fornitura del software sale al terzo posto ed entra una categoria nuova sulla gestione degli errori.
-
A01
Controllo degli accessi violatoBroken Access Control
Un utente vede o modifica dati non suoi, per esempio cambiando
/ordini/1042in/ordini/1043nell’indirizzo.Difesa: controlla i permessi sul server a ogni richiesta e nega tutto per default.
-
A02
Configurazione errataSecurity Misconfiguration
Password di default, pannelli di amministrazione esposti, errori che mostrano lo stack trace, file pubblici per sbaglio.
Difesa: configurazioni minime e ripetibili, niente modalità debug in produzione, header di sicurezza.
-
A03
Catena di fornitura del softwareSoftware Supply Chain Failures
Dipendenze compromesse, pacchetti malevoli, pipeline di build manomesse. Nel 2025 è salita al terzo posto.
Difesa: lockfile, 2FA, aggiornamenti controllati e meno dipendenze (ne parliamo sotto).
-
A04
Errori crittograficiCryptographic Failures
Password salvate in chiaro o con hash veloci come MD5, dati sensibili che viaggiano senza HTTPS.
Difesa: HTTPS ovunque, password con bcrypt, scrypt o Argon2, e mai crittografia fatta in casa.
-
A05
IniezioneInjection
L’input dell’utente diventa codice: SQL injection, comandi di sistema, e anche le XSS che provi qui sotto.
Difesa: query parametrizzate,
textContental posto diinnerHTML, validazione sul server. -
A06
Progettazione insicuraInsecure Design
Il problema è nell’idea, non nel codice: un reset della password con una domanda facile da indovinare, un modulo senza limite di invii.
Difesa: chiediti “come potrebbe essere abusato?” già mentre progetti la funzione.
-
A07
Autenticazione deboleAuthentication Failures
Password deboli accettate, tentativi di login illimitati, sessioni che non scadono mai.
Difesa: 2FA, limite ai tentativi, cookie di sessione
HttpOnlyeSecure. -
A08
Integrità di software e datiSoftware or Data Integrity Failures
Aggiornamenti, script o dati accettati senza verificarne l’origine, come uno script da CDN che può cambiare a tua insaputa.
Difesa: firme e checksum, attributo
integritysugli script esterni. -
A09
Log e allarmi insufficientiSecurity Logging and Alerting Failures
Un attacco va avanti per settimane e nessuno se ne accorge, perché nessuno registra né guarda.
Difesa: registra login falliti e azioni sensibili, con allarmi. E mai password nei log.
-
A10
Gestione errata delle eccezioniMishandling of Exceptional Conditions
Novità del 2025: un errore imprevisto lascia il sistema in uno stato insicuro, per esempio un controllo dei permessi che, se fallisce, lascia passare.
Difesa: in caso di errore nega l’accesso (“fail closed”), gestisci ogni eccezione, mostra messaggi generici.
Prova tu: come nasce un attacco XSS
Il Cross-Site Scripting succede quando un testo scritto da un utente viene trattato come HTML. Scrivi un
commento qui sotto e guarda cosa farebbe il browser con innerHTML e con textContent.
el.innerHTML = commento
el.textContent = commento
Sicuro: il browser mostra il testo così com’è.
Nessun rischio: la demo non esegue nulla. Analizza il testo dentro un <template>, un contenitore
inerte in cui gli script non partono e le immagini non si caricano. Se ti serve davvero accettare HTML dagli
utenti, passalo prima da un sanitizzatore come DOMPurify. Il browser sta imparando a farlo da solo con
setHTML(), già in Chrome e Firefox ma non ancora in Safari.
Il pericolo nelle dipendenze
Un progetto JavaScript medio installa centinaia di pacchetti scritti da persone che non conosci. Dal 2025 questo rischio è diventato molto concreto.
-
Il maintainer di chalk, debug e altri 17 pacchetti cade in un’email di phishing che imita il supporto npm. Per pochi minuti escono versioni che sostituiscono gli indirizzi dei wallet crypto, in pacchetti che insieme contano tra 2 e 3 miliardi di download a settimana. Socket
-
Shai-Hulud, un worm che si replica da solo: ruba i token di npm e GitHub e le chiavi cloud, poi pubblica versioni infette di altri pacchetti. Oltre 500 pacchetti colpiti, con un’allerta della CISA americana. CISA
-
Shai-Hulud 2.0 si attiva già durante l’installazione, nello script
preinstall: 796 pacchetti con oltre 20 milioni di download settimanali. Bastava unnpm install. Datadog Security Labs -
La risposta di npm: i vecchi token “classic” sono stati revocati e il login ora crea sessioni di due ore. Nel 2026 sono arrivate la pubblicazione con approvazione in 2FA (staged publishing) e la scansione antimalware al momento della pubblicazione. GitHub
Come difenderti
- Committa il lockfile e in CI installa con
npm ci: installi esattamente le versioni che hai verificato. - Attiva la verifica in due passaggi su npm, GitHub e sul pannello dell’hosting. Un account senza 2FA è la porta più facile.
- Aggiorna con metodo.
npm audit, Dependabot o Renovate ti avvisano; leggi cosa cambia prima di unire. - Meno dipendenze. Prima di installare un pacchetto chiediti se ti serve davvero. Questo sito non ha nessuna dipendenza a runtime: font e icone vengono inclusi durante la build.
- Attenzione ai nomi.
reacctnon èreact: chi pubblica malware sceglie nomi a una lettera di distanza da quelli famosi. - Script di installazione sotto controllo. Con
npm install --ignore-scriptsi pacchetti non eseguono codice durante l’installazione.
Segreti al sicuro, header al loro posto
Le chiavi non vanno nel codice
Password e chiavi API stanno nelle variabili d’ambiente, in un file .env escluso da Git. E
ricorda: tutto ciò che finisce nel JavaScript del frontend è pubblico, anche se si chiama
SECRET_KEY. Per questo Vite passa al browser solo le variabili che iniziano con
VITE_.
.gitignore
.env
.env.*
!.env.example
Se una chiave è finita in un commit, cancellarla non basta: la cronologia di Git la conserva. Va revocata e rigenerata subito.
Header di sicurezza
Poche righe nella configurazione del server chiudono intere famiglie di attacchi. Esempio nel formato
_headers di Netlify e Cloudflare Pages:
_headers
/*
Content-Security-Policy: default-src 'self'; object-src 'none'; frame-ancestors 'none'
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
- Content-Security-Policy: decide da dove possono arrivare script e risorse. È la seconda linea di difesa contro le XSS.
- Strict-Transport-Security: il browser usa sempre HTTPS, anche se qualcuno scrive http://.
- frame-ancestors 'none': nessun altro sito può incorporare il tuo in un iframe (clickjacking).
Checklist prima del deploy
Spunta man mano. I progressi restano salvati in questo browser.
Completati: 0 di 12