SQL injection e XSS sono tra le vulnerabilità più antiche del web, ma continuano a comparire ogni anno nei bollettini di sicurezza. Hanno una radice comune: un’applicazione che si fida troppo dell’input esterno.

L’idea alla base: quando i dati diventano codice

Ogni applicazione web riceve dati dall’utente: campi di ricerca, moduli di login, commenti, parametri nell’URL, input di plugin o API. Il problema nasce quando questi dati finiscono in un punto in cui vengono interpretati come istruzioni invece che trattati come semplice testo.

È la radice comune di tutte le vulnerabilità di tipo injection, che nell’OWASP Top 10 sono considerate una categoria a sé e comprendono, dal 2021, anche lo XSS.

Cos’è la SQL injection

Quasi ogni sito dinamico salva i propri dati in un database e li legge con query scritte in linguaggio SQL. Se il codice costruisce la query incollando direttamente ciò che scrive l’utente, un attaccante può inserire input costruito apposta in modo che il database lo esegua come parte del comando.

Un esempio semplificato di codice vulnerabile, in PHP:

// SBAGLIATO: l'input dell'utente finisce dritto nella query
$query = "SELECT * FROM utenti WHERE nome = '" . $_GET['nome'] . "'";

Qui il valore di nome non viene mai separato dalla query. Chi controlla quel valore può cambiare il significato dell’intera istruzione. Se qualcuno inserisce come nome il valore ' OR '1'='1, la condizione diventa sempre vera e la query restituisce tutti gli utenti.

Cosa può fare un attaccante (SQL injection)

  • Leggere dati riservati: utenti, indirizzi email, hash delle password, dati di pagamento.
  • Modificare o cancellare dati nel database.
  • Aggirare un login, ottenendo l’accesso senza conoscere la password.
  • In casi gravi, ottenere il controllo del server, se il database o la configurazione lo permettono.

I tipi principali di SQL injection

  • In-band: il risultato dell’attacco compare direttamente nella risposta della pagina.
  • Blind (cieca): l’applicazione non mostra i risultati, ma l’attaccante deduce informazioni da piccoli cambiamenti nel comportamento o nei tempi di risposta.
  • Second-order: l’input malevolo viene salvato e usato più tardi, in un altro punto dell’applicazione.

Come difendersi dalla SQL injection

  • Query parametrizzate (prepared statements): separano il codice SQL dai dati, così l’input non può mai essere interpretato come comando.
  • Mai costruire query incollando stringhe con input dell’utente.
  • Permessi minimi: l’account del database usato dal sito deve poter fare solo ciò che serve.

La stessa query dell’esempio scritta in modo corretto:

// CORRETTO: i dati sono separati dalla query
$stmt = $pdo->prepare("SELECT * FROM utenti WHERE nome = :nome");
$stmt->execute(['nome' => $_GET['nome']]);

Cos’è lo XSS (Cross‑Site Scripting)

Lo XSS è una vulnerabilità che permette a un attaccante di iniettare codice JavaScript dentro una pagina web e farlo eseguire nel browser delle vittime, come se fosse parte legittima del sito.

A differenza della SQL injection, lo XSS non colpisce il database o il server, ma gli utenti che visitano la pagina. È una falla di output non filtrato: il sito mostra contenuto inserito dall’utente senza trasformarlo in semplice testo.

Esempio minimale di XSS

Se un sito mostra questo frammento senza alcun filtro:

<script>alert('XSS');</script>

Il browser lo esegue. È un esempio semplice, ma dimostra che il confine tra testo e codice può essere sottile: se il sito non distingue tra contenuto e script, il browser non lo farà.

Cosa può fare un attaccante con XSS

  • Rubare la sessione dell’utente o altri dati accessibili alla pagina.
  • Compiere azioni a nome della vittima, come pubblicare contenuti o cambiare impostazioni.
  • Mostrare moduli falsi per raccogliere credenziali o dati sensibili.
  • Reindirizzare il visitatore verso un sito malevolo o una pagina di phishing.

I tre tipi di XSS (versione ampliata)

  • Reflected: il codice malevolo arriva in un link o in un parametro e viene “riflesso” nella pagina di risposta. La vittima deve di solito cliccare un link costruito dall’attaccante.
  • Stored: il codice viene salvato dal sito, per esempio in un commento, in un profilo o in un messaggio, e colpisce chiunque visualizzi quella pagina. È il più pericoloso, perché può propagarsi come un worm.
  • DOM-based: la falla è nel JavaScript della pagina, che inserisce dati non controllati nella struttura della pagina direttamente nel browser, usando funzioni come innerHTML, document.write() o eval().

Perché lo XSS è pericoloso

Il codice eseguito tramite XSS gira con gli stessi permessi del sito legittimo. Se l’utente è autenticato, lo script può agire come se fosse lui: leggere dati, inviare richieste, modificare contenuti. In molti casi, un XSS su una pagina admin equivale a prendere il controllo dell’intero sito.

Come difendersi dallo XSS

  • Escape dell’output: convertire i caratteri speciali prima di mostrare un dato in una pagina, così il browser li tratta come testo e non come codice. In ambienti come WordPress si usano funzioni come esc_html(), esc_attr() e wp_kses().
  • Content Security Policy (CSP): una intestazione HTTP che limita da dove e come il browser può eseguire script, riducendo l’impatto di eventuali XSS.
  • Cookie con flag HttpOnly: impediscono agli script di leggere i cookie di sessione, rendendo più difficile rubare l’accesso.
  • Evitare funzioni pericolose nel frontend: non usare innerHTML, document.write(), outerHTML, insertAdjacentHTML() o eval() con input non filtrato.

⚡ La differenza in due righe

  • SQL injection: l’attacco colpisce il server e il suo database.
  • XSS: l’attacco colpisce gli utenti, tramite il loro browser.

Casi famosi

  • Heartland Payment Systems (2008): una SQL injection fu il punto d’ingresso in una delle più grandi violazioni di dati di carte di pagamento mai registrate.
  • Il worm Samy su MySpace (2005): un caso di XSS di tipo stored che si propagò da solo e coinvolse in poche ore oltre un milione di profili.

Esistono ancora? Sì, e perché

Alcune cose sono migliorate. I framework moderni gestiscono in automatico molti casi, le query parametrizzate sono diventate lo standard e i browser offrono protezioni come la Content Security Policy. Ma queste vulnerabilità restano diffuse per alcuni motivi:

  • molte applicazioni, plugin e temi sono scritti in fretta e senza controlli sull’input;
  • il software vecchio non viene aggiornato e resta vulnerabile per anni;
  • ogni nuovo punto in cui l’utente inserisce dati è un possibile errore, e basta dimenticarne uno.

Come difendersi (visione d’insieme)

Contro la SQL injection

  • Query parametrizzate (prepared statements): separano il codice SQL dai dati.
  • Mai costruire query incollando stringhe con input dell’utente.
  • Permessi minimi: l’account del database usato dal sito deve poter fare solo ciò che serve.

Contro lo XSS

  • Escape dell’output: trattare sempre l’output come potenzialmente pericoloso.
  • CSP: limitare le fonti di script.
  • Cookie HttpOnly: proteggere la sessione dagli script.

In generale

  • validare sempre l’input, lato server e non solo nel browser;
  • tenere aggiornati sistemi, librerie e plugin;
  • usare un WAF (Web Application Firewall) come ulteriore livello di protezione, senza considerarlo sostitutivo di un codice sicuro.

E con WordPress?

Il core di WordPress è mantenuto con attenzione, ma gran parte delle falle note riguarda plugin e temi, soprattutto quelli poco aggiornati o scritti senza attenzione alla sicurezza.

  • aggiorna sempre core, plugin e temi;
  • elimina ciò che non usi, perché anche un plugin disattivato resta sul server;
  • preferisci plugin con aggiornamenti recenti e molte installazioni attive;
  • se sviluppi codice, usa $wpdb->prepare() per le query e funzioni come esc_html(), esc_attr() e wp_kses() per l’output.

In conclusione

SQL injection e XSS sono vecchie di decenni, ma restano attuali perché nascono da un errore umano facile da commettere: fidarsi di ciò che arriva dall’esterno. La regola per evitarle è semplice da enunciare e meno da applicare sempre: trattare ogni input come non attendibile e tenere sempre separati i dati dal codice.