SQL Injection è un tipo di attacco al database di un'applicazione in cui un attaccante inietta codice SQL malizioso nei parametri di query, ottenendo accesso non autorizzato ai dati o la possibilità di modificarli. Secondo OWASP (2025), SQL Injection rimane una delle vulnerabilità critiche in grado di portare a un compromesso completo del database. L'iniezione di codice SQL consente all'attaccante di leggere, modificare ed eliminare record e, in alcuni casi, di ottenere l'accesso al sistema operativo del server.
Punti chiave
SQL Injection è una vulnerabilità che si verifica quando un'applicazione costruisce query SQL tramite concatenazione di stringhe con dati utente. Un attaccante passa una stringa appositamente costruita in un parametro di query che modifica la struttura del comando SQL. Invece di essere dati, la stringa iniettata diventa parte del codice SQL, consentendo all'attaccante di eseguire query arbitrarie contro il database. Secondo il Rapporto sulle violazioni dei dati di Verizon (2025), SQL Injection è presente nell'8% di tutte le violazioni dei dati investigate.
Nonostante sia una vulnerabilità ben nota (prima menzione alla fine degli anni '90), SQL Injection si trova ancora nelle applicazioni moderne. Il motivo è l'errore umano: gli sviluppatori scrivono codice con concatenazione di stringhe, il codice legacy non viene rifattorizzato e i framework ORM vengono usati in modo errato (ad esempio, query raw con interpolazione di stringhe). Secondo Veracode (2026), circa il 14% di tutte le applicazioni scansionate contiene almeno una vulnerabilità SQLi.
Un'iniezione SQL riuscita offre all'attaccante un'ampia gamma di capacità: leggere qualsiasi tabella del database, inclusi hash di password e dati personali degli utenti; modificare ed eliminare record; eseguire operazioni amministrative (DROP TABLE, TRUNCATE); e in alcune configurazioni, l'esecuzione remota di comandi tramite xp_cmdshell (MSSQL) o INTO OUTFILE (MySQL). Le conseguenze vanno dalla fuga di dati utente alla perdita totale del controllo del sistema.
Le iniezioni SQL sono classificate in base al metodo di estrazione dei dati dal database. La scelta del metodo dipende da come l'applicazione gestisce i risultati delle query e i messaggi di errore. La classificazione OWASP identifica tre tipi principali: In-band (dati estratti attraverso lo stesso canale), Inferential/Blind (inferenze logiche) e Out-of-band (dati trasmessi attraverso un altro canale).
| Tipo | Metodo di estrazione | Complessità | Frequenza |
|---|---|---|---|
| In-band (classico) | Direttamente attraverso il risultato della query | Bassa | Alta |
| Blind SQLi | Inferenze logiche dalle risposte del server | Alta | Media |
| Out-of-band | Attraverso un canale esterno (DNS, HTTP) | Media | Bassa |
Il tipo più comune. Un attaccante inietta codice SQL in un parametro di query e il risultato dell'iniezione è direttamente visibile nella risposta del server. Due sottotipi: Error-based (attraverso messaggi di errore del DB) e UNION-based (attraverso l'operatore UNION SELECT). Error-based utilizza le informazioni dai messaggi di errore, come un errore di sintassi MySQL che può rivelare il nome della tabella o la struttura della query. UNION-based consente di combinare i risultati di query legittime con dati di altre tabelle del database.
Utilizzata quando l'applicazione non mostra i risultati della query o i messaggi di errore. Un attaccante pone domande sì/no inviando query con condizioni logiche e analizzando le differenze nelle risposte del server (ad esempio, tempo di risposta o contenuto della pagina). Time-based Blind SQLi utilizza funzioni di ritardo (SLEEP, WAITFOR DELAY) per confermare le condizioni — se la pagina impiega più tempo a caricare, la condizione è vera. Questo metodo è molto lento — l'estrazione di un singolo record può richiedere ore.
# Esempio di Blind SQL Injection (basata sul tempo)
# Se SQLi è vulnerabile, SLEEP(2) viene eseguito se la condizione è soddisfatta
import requests
import time
payload = "' OR IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(2),0) -- "
url = "http://example.com/user?id=1" + payload
start = time.time()
response = requests.get(url)
elapsed = time.time() - start
# Se la risposta arriva dopo >2 secondi — la prima lettera della password è 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")
I dati vengono trasmessi non attraverso la risposta HTTP ma tramite canali alternativi: query DNS, richieste HTTP a un server esterno, SMTP. Utilizzata quando l'applicazione non restituisce i risultati della query né mostra errori. MySQL supporta la funzione LOAD_FILE(), che può attivare una query DNS, e MSSQL ha xp_dirtree per inviare dati a un server SMB remoto. Out-of-band SQL Injection è efficace ma richiede configurazione aggiuntiva del server attaccante e funzioni DB specifiche.
Il meccanismo di SQL Injection si basa sul fatto che SQL utilizza virgolette per i letterali di stringa. Se un'applicazione inserisce l'input utente direttamente in una query SQL senza escapparlo, un attaccante può “chiudere” la stringa e aggiungere codice SQL arbitrario. Ad esempio, nella query SELECT * FROM users WHERE name = '$input', inserire ' OR '1'='1 la trasforma in SELECT * FROM users WHERE name = '' OR '1'='1', che restituisce tutti gli utenti.
Consideriamo un modulo di login con la query SELECT * FROM users WHERE username = '$user' AND password = '$pass'. Se un attaccante inserisce admin' -- nel campo nome utente e lascia la password vuota, la query risultante diventa SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. I caratteri -- commentano il resto della query, disattivando il controllo della password. Il server restituisce il record dell'utente admin e l'attaccante accede senza conoscere la password.
# Esempio di SQL Injection — Bypass dell'autenticazione
# CODICE VULNERABILE: concatenazione diretta di stringhe
def login_vulnerable(username, password):
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)
return cursor.fetchone() is not None
# username = "admin' --" annulla il controllo della password
# VERSIONE SICURA: query parametrizzata
def login_secure(username, password):
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, password))
return cursor.fetchone() is not None
L'operatore UNION SELECT consente di combinare i risultati di due query SELECT. Se un attaccante trova un parametro vulnerabile, aggiunge UNION SELECT con una query da un'altra tabella. Esempio: ' UNION SELECT username, password FROM admins --. La condizione di successo è che il numero di colonne in entrambe le query deve corrispondere. Il numero di colonne viene determinato tramite ORDER BY (inserendo ' ORDER BY 1--, poi 2, 3... fino a un errore). Conoscendo il numero di colonne, l'attaccante inserisce UNION SELECT con lo stesso numero di campi.
Le applicazioni mobili affrontano SQL Injection sia lato server (API) che lato client — nei database locali (SQLite, Realm). Mentre la SQLi lato server nelle API mobili è simile alle applicazioni web, i database locali creano un vettore aggiuntivo. Se un'applicazione memorizza dati in SQLite ed esegue query con concatenazione di stringhe, i dati maliziosi che entrano nel database locale tramite l'API possono attivare SQLi durante l'elaborazione successiva.
Il database locale SQLite su un dispositivo è anch'esso vulnerabile a SQL Injection se l'applicazione costruisce query concatenando stringhe. I Content Providers su Android e Core Data su iOS utilizzano la parametrizzazione per impostazione predefinita, ma le query raw richiedono l'attenzione dello sviluppatore. SQLite non supporta query multiple separate da punto e virgola, il che limita le opzioni dell'attaccante ma non protegge dalla lettura dei dati tramite condizioni WHERE. Utilizzare sempre selectionArgs in Android e NSPredicate con parametri in iOS.
L'API con cui un'applicazione mobile comunica è vulnerabile come qualsiasi server web. Gli sviluppatori mobili spesso assumono che SQLi sia solo un problema del backend, ma la vulnerabilità si verifica all'endpoint dell'API che accetta parametri dal client. La separazione delle responsabilità non protegge: se lo sviluppatore backend ha dimenticato di parametrizzare la query, l'app mobile dell'utente diventa un vettore d'attacco. Richiedere al backend l'uso di ORM o prepared statements.
// Esempio di SQL Injection in SQLite locale su Android
// CODICE VULNERABILE: concatenazione diretta
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = $userId", null
)
}
// VERSIONE SICURA: parametrizzazione tramite selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = ?",
arrayOf(userId)
)
}
La protezione contro SQL Injection si basa su un principio semplice: non fidarsi mai dell'input utente nelle query SQL. L'unico metodo affidabile è la parametrizzazione delle query (prepared statements), dove il codice SQL e i dati vengono passati separatamente. Tutti gli altri metodi — escaping, validazione, WAF — sono livelli di sicurezza aggiuntivi ma non sostituiscono la parametrizzazione. Secondo OWASP (2025), la parametrizzazione previene il 100% degli attacchi SQL Injection.
Utilizzando prepared statements, la query SQL viene prima compilata dal server DB senza dati, e poi i valori dei parametri vengono passati separatamente. Il database tratta i parametri come dati, non come codice eseguibile. Anche se un attaccante passa ' OR '1'='1, il database lo interpreta come un valore stringa, non come codice SQL. I prepared statements sono supportati da tutti i linguaggi e framework moderni: PDO in PHP, PreparedStatement in Java, cursor.execute in Python.
Gli ORM moderni (Hibernate, Entity Framework, SQLAlchemy, Room) utilizzano automaticamente la parametrizzazione durante l'esecuzione delle query, a meno che lo sviluppatore non passi a query raw. Tuttavia, gli ORM non proteggono completamente: costrutti come @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) in JPA richiedono il passaggio di parametri tramite parametri nominati, non per concatenazione. I Query Builder (Knex, jOOQ) parametrizzano anch'essi le query per impostazione predefinita se non vengono utilizzati metodi raw.
L'escaping di caratteri speciali (mysql_real_escape_string) è un metodo obsoleto che non protegge da tutti i tipi di SQL Injection. Il problema: l'escaping dipende dalla codifica e può essere bypassato utilizzando codifiche multibyte (ad esempio, GBK nei sistemi asiatici). Utilizzare l'escaping solo in codice legacy dove la parametrizzazione è impossibile, e sempre in combinazione con una rigorosa validazione del tipo di input.
| Metodo | Efficacia | Raccomandazione |
|---|---|---|
| Prepared Statements | 100% | Obbligatorio per tutte le query |
| ORM (uso corretto) | 99% | Raccomandato |
| Escaping di stringhe | 70% (dipende dalla codifica) | Solo legacy |
| Validazione input (white-list) | 50% (solo numeri) | Aggiuntivo |
| WAF (Web Application Firewall) | 60% | Aggiuntivo |
L'account dell'applicazione dovrebbe avere i privilegi minimi necessari: SELECT, INSERT, UPDATE, DELETE — solo sulle tabelle di cui l'applicazione ha effettivamente bisogno. Vietare l'uso di DROP, TRUNCATE, CREATE per l'account dell'applicazione. Questo limita i danni anche in caso di iniezione SQL riuscita: l'attaccante non potrà eliminare tabelle o eseguire operazioni amministrative.
I test regolari di SQL Injection dovrebbero far parte del pipeline di sviluppo sicuro. Una combinazione di analisi statica, scansione dinamica e test di penetrazione manuali offre i migliori risultati. Secondo il Rapporto sulla cybersecurity di Synopsys (2025), gli scanner automatizzati trovano fino al 70% delle vulnerabilità SQLi, ma gli attacchi Blind complessi richiedono test manuali.
Per le applicazioni mobili, anche l'analisi di SQLite locale è importante: verificare tutte le chiamate rawQuery, le query ContentProvider e le query Room con rawQuery. Strumenti: Android Studio Lint (rileva SQLi in SQLite), MobSF (Mobile Security Framework) per l'analisi statica e dinamica automatica di APK/IPA. Si raccomanda anche di testare gli endpoint API tramite sqlmap con intercettazione proxy del traffico dell'applicazione mobile.
Domande frequenti
SQL Injection attacca i database relazionali tramite query SQL. NoSQL Injection colpisce i database non relazionali (MongoDB, Couchbase) attraverso i loro operatori di query ($gte, $ne, $where). In MongoDB, l'iniezione è possibile se l'applicazione costruisce un documento BSON da una stringa JSON. I meccanismi di protezione sono simili: parametrizzazione e validazione dei tipi.
Trovare tutti i punti in cui le query SQL sono formate tramite concatenazione di stringhe con dati utente. Cercare pattern come "SELECT ... WHERE id = " + userId o f"UPDATE ... SET name = '{name}'". Ogni riga di questo tipo è una potenziale iniezione SQL. Sostituirle tutte con query parametrizzate o prepared statements.
I framework ORM proteggono automaticamente solo se si utilizzano i loro metodi Query Builder e parametri nominati. Se si utilizzano query raw (nativeQuery in JPA, rawQuery in Room), la protezione non funziona — è necessario passare i parametri tramite espressioni preparate, non tramite concatenazione di stringhe.
Second-Order SQL Injection è un attacco in cui dati maliziosi vengono memorizzati nel database come sicuri, ma poi utilizzati in un'altra query senza escaping. Ad esempio, un attaccante si registra con un nome utente come ' OR '1'='1. I dati vengono salvati come stringa — non c'è attacco al momento della registrazione. Ma se un'altra query utilizza il nome utente in SQL senza parametrizzazione, l'iniezione si attiva.
NoSQL Injection può essere potenzialmente più pericoloso a causa della minore consapevolezza degli sviluppatori. Gli sviluppatori conoscono SQL Injection e la maggior parte usa ORM, ma pochi conoscono NoSQL Injection. In MongoDB, una query malformata può restituire tutti i documenti di una collezione. La protezione è la stessa — prepared statements (parametrizzazione BSON) e rigorosa validazione dell'input.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche