Salta al contenuto
PURPLEDUEL

SQL injection: cos'è e come difendersi

Aggiornato il · 2 min di lettura

La SQL injection è una vulnerabilità delle applicazioni web che nasce quando il testo inserito da un utente viene incollato dentro una query SQL senza precauzioni, così che il database lo esegua come parte del comando invece che come semplice dato. Per anni è stata fra le falle più diffuse e dannose; la buona notizia è che la difesa è semplice e conosciuta.

Perché succede

Un database relazionale riceve istruzioni scritte in SQL, per esempio «dammi la riga della tabella utenti con questo nome». Molte applicazioni costruiscono queste istruzioni unendo pezzi di testo, compresi quelli scritti dall'utente in un modulo, in un parametro dell'indirizzo o in un cookie. Il problema sta nel mescolare codice (la struttura della query) e dati (ciò che l'utente ha digitato): se il database non può distinguerli, chi scrive il dato può cambiare il comando.

Un esempio concreto, a livello di concetto

Ecco un frammento volutamente vulnerabile, in pseudocodice, di un modulo di accesso:

nome = richiesta.campo("nome")
query = "SELECT * FROM utenti WHERE nome = '" + nome + "'"
risultato = database.esegui(query)

Se l'utente scrive anna, la query è quella prevista. Ma il programma non sa distinguere un nome da un pezzo di istruzione: se nel campo compaiono apici e parole chiave SQL, la struttura della query cambia, e con essa la logica del sito. Le conseguenze possibili sono leggere righe che non andrebbero mostrate, saltare un controllo di accesso o alterare i dati. Il difetto non è nel database: è nell'aver incollato insieme codice e dati.

Provare queste tecniche su un sito reale senza permesso scritto è un reato. Per esercitarti usa solo ambienti creati apposta, come laboratori e applicazioni volutamente vulnerabili in locale.

Come ci si difende

  1. Query parametrizzate (prepared statement). La query con i segnaposto viaggia separata dai valori, e il database tratta i valori sempre e solo come dati, qualunque carattere contengano.
  2. ORM e librerie moderne, che parametrizzano per impostazione predefinita. Attenzione ai punti in cui si scrive SQL «grezzo» a mano.
  3. Validare l'input: tipo, lunghezza e formato attesi (un identificativo numerico deve essere un numero). È un controllo in più, non un sostituto delle query parametrizzate.
  4. Privilegio minimo per l'utente del database: l'applicazione non deve poter cancellare tabelle né leggere ciò che non le serve. Vedi la guida al privilegio minimo.
  5. Errori generici verso l'utente, dettagli solo nei log: i messaggi d'errore troppo ricchi aiutano chi attacca a capire come è fatta la query.
  6. Web application firewall come rete di sicurezza, non come soluzione: filtra i tentativi più banali ma non ripara il codice.
  7. Revisione del codice e test di sicurezza autorizzati, con scanner e prove periodiche, per trovare i punti dove la query è ancora costruita a mano.

La versione corretta del frammento di prima è questa: la struttura è fissa, il valore viaggia a parte.

query = "SELECT * FROM utenti WHERE nome = ?"
risultato = database.esegui(query, [nome])

Perché è un errore tanto frequente

Concatenare testo sembra la strada più rapida, funziona nei test fatti con dati normali e il difetto resta invisibile finché qualcuno non prova input insoliti. Per questo la regola di squadra più efficace è semplice: nessuna query viene mai costruita unendo testo che arriva dall'esterno.

Continua a leggere