Best practice di sicurezza per sviluppatori web
La sicurezza web non è opzionale. Con minacce informatiche in aumento, gli sviluppatori devono integrare la sicurezza in ogni livello delle loro applicazioni. Questa guida copre le pratiche essenziali da implementare oggi.
Sicurezza delle password
Non memorizzare mai password in chiaro
Calcola sempre l’hash delle password usando algoritmi moderni come bcrypt, Argon2 o scrypt. Questi algoritmi sono progettati per essere lenti, rendendo gli attacchi di forza bruta non praticabili.
// Buono: usare bcrypt
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash(password, 12);
Confronto degli algoritmi di hashing
Non tutti gli algoritmi di hashing sono uguali. Scegliere quello giusto dipende dal tuo modello di minaccia e dal caso d’uso:
| Algoritmo | Dimensione output | Velocità | Caso d’uso | Stato di sicurezza |
|---|---|---|---|---|
| MD5 | 128 bit | Molto veloce | Checksum, hash non legati alla sicurezza | Compromesso per la sicurezza |
| SHA-256 | 256 bit | Veloce | Integrità dati, firme digitali | Sicuro |
| bcrypt | 184 bit | Lento (regolabile) | Hashing password | Sicuro |
| Argon2 | Configurabile | Lento (regolabile) | Hashing password (moderno) | Raccomandato per nuovi progetti |
bcrypt e Argon2 sono deliberatamente lenti — è una caratteristica, non un bug. Ogni operazione di hash richiede decine o centinaia di millisecondi, rendendo gli attacchi di forza bruta su larga scala economicamente non praticabili.
Capire l’entropia delle password
La forza delle password si può misurare matematicamente con l’entropia: entropia = log2(charset_size^length). Una password che usa lettere minuscole (26 caratteri) con 8 caratteri ha ~37.6 bit di entropia. Una password di 16 caratteri che mescola maiuscole, minuscole, cifre e simboli (95 caratteri) ha ~105 bit — esponenzialmente più difficile da craccare. Per questo la lunghezza conta più della complessità per la maggior parte degli utenti. Per un approfondimento sulla matematica dietro la forza delle password, vedi la nostra guida entropia delle password spiegata.
Usa un password manager
Raccomanda ai tuoi utenti di adottare un password manager. Le password scelte da umani tendono a seguire pattern prevedibili che gli attaccanti sfruttano con attacchi a dizionario. I password manager generano stringhe veramente casuali ed eliminano il riutilizzo delle password tra servizi — uno dei vettori più comuni per attacchi di credential stuffing.
Usa un numero sufficiente di salt round
I salt round determinano il costo computazionale. Più alto è più sicuro ma più lento. 10-12 round sono un buon equilibrio per la maggior parte delle applicazioni.
Validazione dell’input
Valida sia lato client sia lato server
La validazione lato client migliora la UX, ma la validazione lato server è essenziale per la sicurezza. Non fidarti mai dell’input del client.
Sanifica tutto l’input utente
Previeni gli attacchi di iniezione sanificando l’input:
- Usa query parametrizzate per SQL
- Esegui l’escape dell’output HTML per prevenire XSS
- Valida rigorosamente i caricamenti di file
Esempi concreti di attacchi
Capire gli attacchi reali aiuta a difendersi. Considera un form di commenti che renderizza l’input utente direttamente in HTML. Un attaccante invia:
<script>alert('xss')</script>
Se l’applicazione lo renderizza senza escape, lo script viene eseguito nel browser di ogni visitatore — rubando cookie, reindirizzando utenti o iniettando keylogger. Il fix: codifica sempre l’output contestualmente. Usa librerie come DOMPurify per la sanificazione HTML.
L’SQL injection è altrettanto pericolosa. In un form di login, un attaccante inserisce come username:
' OR 1=1 --
Se la query è costruita con concatenazione di stringhe ("SELECT * FROM users WHERE username='" + input + "'") questo aggira completamente l’autenticazione. I -- commentano il resto della query. Il fix: usa sempre query parametrizzate (chiamate anche prepared statement). Ogni libreria di database principale le supporta:
// SBAGLIATO: concatenazione di stringhe
db.query(`SELECT * FROM users WHERE username='${input}'`);
// CORRETTO: query parametrizzata
db.query('SELECT * FROM users WHERE username = $1', [input]);
Content Security Policy (CSP)
Come difesa in profondità, distribuisci header Content Security Policy. CSP dice al browser quali sorgenti di contenuto sono affidabili, bloccando di fatto script inline e caricamento di risorse non autorizzate. Anche se nel tuo codice esiste una vulnerabilità XSS, una CSP rigorosa può impedire l’esecuzione dello script iniettato. Inizia con Content-Security-Policy: default-src 'self' e aggiungi gradualmente eccezioni secondo necessità.
Funzioni hash
Scegliere l’hash giusto
Casi d’uso diversi richiedono funzioni hash diverse:
| Caso d’uso | Raccomandato |
|---|---|
| Password | bcrypt, Argon2 |
| Integrità | SHA-256 |
| Checksum | SHA-256, MD5 (non sicurezza) |
| Hashing veloce | BLAKE3 |
Capire output e collisioni dell’hash
MD5 produce un hash a 128 bit (32 caratteri hex), mentre SHA-256 produce un hash a 256 bit (64 caratteri hex). Questa differenza conta: uno spazio di output più grande significa esponenzialmente più valori hash possibili, rendendo le collisioni molto meno probabili. Una collisione si verifica quando due input diversi producono lo stesso hash — un attaccante in grado di generare collisioni può falsificare firme digitali o manomettere dati verificati.
Le collisioni MD5 possono essere generate in pochi secondi su hardware moderno. SHA-256 rimane resistente alle collisioni senza attacchi pratici noti. Per questo è critico scegliere l’algoritmo giusto per il contesto giusto:
- Checksum e deduplicazione: MD5 è accettabile quando la sicurezza non è una preoccupazione
- Integrità dei dati e firme: SHA-256 fornisce forte resistenza alle collisioni
- Memorizzazione delle password: bcrypt o Argon2, che aggiungono salt e lentezza deliberata
HMAC per l’autenticazione dei messaggi
Quando devi verificare sia integrità sia autenticità di un messaggio, usa HMAC (Hash-based Message Authentication Code). HMAC combina una funzione hash con una chiave segreta, garantendo che solo le parti che conoscono la chiave possano generare o verificare il tag. È essenziale per autenticazione API, verifica di webhook e generazione sicura di token.
Non usare mai MD5 o SHA-1 per la sicurezza
MD5 e SHA-1 sono compromessi per scopi di sicurezza. Usa SHA-256 o SHA-3 per l’hashing crittografico.
HTTPS ovunque
Cosa fa effettivamente TLS
TLS (Transport Layer Security) fornisce tre protezioni critiche: crittografia in transito (impedisce l’intercettazione), autenticazione del server (dimostra che stai parlando con il server reale, non con un impostore) e integrità dei dati (rileva qualsiasi manomissione durante la trasmissione). Senza TLS, ogni dato tra i tuoi utenti e il tuo server — password, token, informazioni personali — viaggia in chiaro.
Usa sempre TLS
- Ottieni certificati da CA affidabili (Let’s Encrypt è gratuito e completamente automatizzato)
- Reindirizza HTTP a HTTPS
- Usa header HSTS
- Tieni aggiornate le versioni TLS
HSTS e contenuto misto
Gli header HTTP Strict Transport Security (HSTS) dicono ai browser di connettersi solo via HTTPS, anche se l’utente digita http://. Imposta Strict-Transport-Security: max-age=31536000; includeSubDomains per applicarlo per un anno intero su tutti i sottodomini. Questo previene gli attacchi di SSL stripping in cui un attaccante declassa una connessione a HTTP.
Attenzione agli avvisi di contenuto misto: se la tua pagina HTTPS carica immagini, script o fogli di stile via HTTP, i browser li bloccheranno o avviseranno. Verifica le tue pagine per URL http:// cablati e usa percorsi protocol-relative o forza HTTPS per tutte le risorse.
Autenticazione
Implementa il rate limiting
Previeni attacchi di forza bruta con il rate limiting:
- Limita i tentativi di login per IP
- Aggiungi ritardi dopo i tentativi falliti
- Usa CAPTCHA per attività sospette
Basi dell’autenticazione JWT
I JSON Web Token (JWT) forniscono un meccanismo di autenticazione stateless strutturato come header.payload.signature. Il server firma il token con una chiave segreta, e i client lo includono nelle richieste successive. Poiché il token contiene i claim dell’utente, il server non deve cercare lo stato di sessione a ogni richiesta — il che rende i JWT adatti a sistemi distribuiti e microservizi.
Imposta sempre tempi di scadenza brevi sui token di accesso (es. 15 minuti) e usa refresh token per ottenere nuovi token di accesso. Memorizza i refresh token in modo sicuro (cookie httpOnly, non localStorage) e implementa la rotazione dei token in modo che ogni refresh token possa essere usato una sola volta.
Autenticazione a più fattori (MFA)
L’MFA non è più opzionale per qualsiasi applicazione seria. Richiedere un secondo fattore — codici TOTP (Google Authenticator), chiavi hardware (YubiKey) o notifiche push — riduce drasticamente l’impatto di password compromesse. Anche se un attaccante ottiene credenziali valide, non può autenticarsi senza il secondo fattore.
Prevenzione del session fixation
Gli attacchi di session fixation si verificano quando un attaccante imposta un session ID noto prima che l’utente si autentichi. Dopo il login, l’attaccante usa lo stesso session ID per dirottare la sessione autenticata. Previenilo rigenerando sempre il session ID dopo l’autenticazione riuscita e invalidando quello vecchio.
Usa una gestione sicura delle sessioni
- Genera session ID crittograficamente casuali
- Imposta i flag secure e httpOnly sui cookie
- Implementa il timeout di sessione
- Invalida le sessioni al logout
Checklist degli header di sicurezza
Distribuire i giusti header di risposta HTTP è uno dei modi più efficaci e a basso sforzo per rafforzare la tua applicazione. Ecco una tabella di riferimento rapida degli header di sicurezza essenziali:
| Header | Scopo | Valore esempio |
|---|---|---|
| Content-Security-Policy | Prevenire XSS e iniezione di dati | default-src 'self' |
| Strict-Transport-Security | Forzare connessioni HTTPS | max-age=31536000; includeSubDomains |
| X-Content-Type-Options | Prevenire MIME type sniffing | nosniff |
| X-Frame-Options | Prevenire clickjacking | DENY |
| Referrer-Policy | Controllare le informazioni di referrer | strict-origin-when-cross-origin |
Questi header possono essere impostati a livello di web server (Nginx, Apache), a livello CDN/edge (Cloudflare, Vercel), o all’interno del framework della tua applicazione. Verifica i tuoi header con strumenti come securityheaders.com. Punta a una valutazione A+ — la maggior parte di questi header è una singola riga di configurazione e non costa nulla distribuirli.
Usare i nostri strumenti di sicurezza
Esplora i nostri strumenti di sicurezza per aiutarti nello sviluppo:
- Generatore Hash MD5 - Per checksum e sistemi legacy
- Generatore UUID - Per identificatori casuali sicuri
- Generatore di Password Casuali - Per generare password forti
Per una panoramica più ampia su come gli strumenti di codifica, hashing e conversione si inseriscono nel tuo flusso di sviluppo, vedi la nostra Guida agli Strumenti Essenziali per Sviluppatori.
Domande frequenti
Qual è la vulnerabilità di sicurezza web più comune?
Cross-Site Scripting (XSS) rimane la vulnerabilità web più diffusa secondo OWASP. Si verifica quando le applicazioni includono dati non affidabili nelle pagine web senza una validazione adeguata. Previeni XSS sanificando tutto l’input utente, usando header Content Security Policy e codificando l’output in base al contesto (HTML, JavaScript, URL o CSS).
MD5 è ancora sicuro per l’hashing delle password?
No — MD5 non dovrebbe mai essere usato per l’hashing delle password. È computazionalmente veloce, il che lo rende vulnerabile ad attacchi di forza bruta e rainbow table. Le GPU moderne possono calcolare miliardi di hash MD5 al secondo. Usa invece bcrypt, scrypt o Argon2, che sono deliberatamente lenti e includono salt integrato per resistere agli attacchi.
Quanto dovrebbe essere lunga una password sicura nel 2026?
Si raccomanda un minimo di 12 caratteri, ma 16+ caratteri forniscono una protezione significativamente più forte. La lunghezza conta più della complessità — una passphrase di 20 caratteri come “correct-horse-battery-staple” è più forte di una password breve e complessa come “P@ss1!”. Abilita l’autenticazione a più fattori (MFA) indipendentemente dalla lunghezza della password per gli account critici.
Qual è la differenza tra crittografia e hashing?
La crittografia è reversibile — puoi decrittare i dati alla loro forma originale usando una chiave. L’hashing è a senso unico — non puoi recuperare i dati originali da un hash. Usa la crittografia per dati che devi recuperare (come dati utente memorizzati), e l’hashing per dati che devi solo verificare (come password e checksum).
Dovrei implementare il mio sistema di autenticazione?
No — costruire l’autenticazione da zero è rischioso e soggetto a errori. Usa framework e servizi collaudati come Auth0, Firebase Auth o Supabase Auth. Questi gestiscono hashing delle password, gestione delle sessioni, rotazione dei token, MFA e protezione dalla forza bruta. Concentra il tuo tempo di sviluppo sulle funzionalità uniche della tua applicazione.
Conclusione
La sicurezza è un processo continuo, non un’attività una tantum. Resta aggiornato sulle ultime vulnerabilità, fai audit regolari del tuo codice e segui il principio del minimo privilegio. I tuoi utenti si fidano di te con i loro dati - onora quella fiducia con pratiche di sicurezza robuste.