La SQL Injection (SQLi) è una classe di vulnerabilità che permette a un attaccante di inserire — o iniettare — codice SQL arbitrario all’interno di una query che l’applicazione costruisce dinamicamente a partire da input utente non sanificato. Il risultato è che il database esegue query diverse da quelle previste dallo sviluppatore, con conseguenze che vanno dall’estrazione non autorizzata di dati alla cancellazione dell’intero database, fino all’esecuzione di comandi sul sistema operativo sottostante. È costantemente nella OWASP Top 10 e rappresenta una delle vulnerabilità più diffuse e pericolose del web.
Meccanismo di base
Considera un’applicazione che costruisce una query di autenticazione così:
SELECT * FROM utenti WHERE username = '$input_utente' AND password = '$input_password'
Se l’utente inserisce come username il valore admin' --, la query diventa:
SELECT * FROM utenti WHERE username = 'admin' --' AND password = '...'
Il doppio trattino (--) è un commento in SQL: tutto ciò che segue viene ignorato. La query autentica l’utente admin senza verificare la password. La causa è la mancata distinzione tra dati e codice nella costruzione della query.
Varianti principali
In-band SQLi: l’attaccante ottiene i risultati direttamente nella risposta HTTP. Le varianti sono error-based (i messaggi di errore rivelano la struttura del DB) e union-based (usa UNION SELECT per aggiungere righe al risultato con dati estratti da altre tabelle).
Blind SQLi: l’applicazione non restituisce risultati o errori espliciti, ma si comporta diversamente a seconda che la condizione iniettata sia vera o falsa. L’attaccante inferisce informazioni un bit alla volta (boolean-based) o misurando i tempi di risposta (time-based, es. SLEEP(5) in MySQL).
Out-of-band SQLi: l’attaccante esfiltra dati attraverso canali separati (es. richieste DNS o HTTP innescate da funzioni del DB come xp_cmdshell in MSSQL o UTL_HTTP in Oracle). Usata quando le varianti in-band non sono praticabili.
Escalation: da lettura a RCE
In configurazioni permissive, SQLi può escalare a Remote Code Execution (RCE). In MySQL, LOAD DATA INFILE e INTO OUTFILE permettono rispettivamente di leggere e scrivere file sul filesystem del server. In MSSQL, la stored procedure xp_cmdshell esegue comandi di sistema operativo. Questi vettori richiedono privilegi elevati sull’utente del database — un motivo per cui il principio del minimo privilegio si applica anche agli account di accesso al DB.
Contromisure
La difesa principale e unica veramente efficace è l’uso di prepared statement (o parameterized query): la query viene compilata con segnaposto, e i valori utente sono passati separatamente come parametri tipizzati. Il driver del database li tratta come dati puri, mai come codice SQL.
# Sbagliato (vulnerabile)
query = f"SELECT * FROM utenti WHERE id = {user_id}"
# Corretto
cursor.execute("SELECT * FROM utenti WHERE id = %s", (user_id,))
L’input validation e l’escaping sono difese parziali — non sostituiscono i prepared statement. Un ORM ben configurato usa prepared statement internamente, ma va comunque usato correttamente (query raw nell’ORM possono reintrodurre la vulnerabilità). Le Web Application Firewall (WAF) possono bloccare pattern SQLi noti, ma sono agggirabili e non vanno considerate la linea di difesa primaria.