Un'iniezione SQL si verifica quando un attaccante riesce a inserire codice SQL arbitrario in una query destinata al database, sfruttando un campo di modulo, un parametro URL, o qualsiasi altro punto di ingresso il cui contenuto viene integrato direttamente nella query senza precauzioni. Questa falla, nota da decenni, resta una delle più frequentemente sfruttate sul web, in particolare negli sviluppi PHP su misura dove la query SQL viene costruita manualmente anziché tramite un framework che impone una protezione predefinita.

Cosa rende vulnerabile una query

Il problema emerge non appena un valore fornito dall'utente viene concatenato direttamente in una stringa che forma la query SQL. Se un campo di ricerca accetta qualsiasi testo e quel testo viene inserito così com'è nella query, un visitatore malintenzionato può inserire un valore che modifica la struttura logica della query stessa, invece di limitarsi a fornire un dato da cercare. Questa manipolazione può permettere di aggirare un'autenticazione, estrarre dati a cui l'utente non dovrebbe avere accesso, o nei casi più gravi, modificare o eliminare dati dal database.

Perché l'escaping manuale non basta

Una reazione comune di fronte a questo rischio consiste nel tentare di ripulire o escapare manualmente i caratteri speciali prima di inserirli nella query, ad esempio escapando gli apostrofi. Questo approccio resta fragile: dipende dalla memoria e dal rigore dello sviluppatore su ogni punto di ingresso del sito, e una sola dimenticanza basta a riaprire la falla. Può inoltre rivelarsi incompleto a seconda del contesto d'uso del dato all'interno della query, il che lo rende una protezione incerta piuttosto che una soluzione solida.

Le query preparate, una separazione netta tra codice e dato

Le query preparate, disponibili nativamente in PHP tramite le estensioni PDO o MySQLi, risolvono questo problema alla radice separando completamente la struttura della query SQL dai valori che vi vengono inseriti. La query viene prima inviata al database in una forma fissa, con segnaposto riservati per i valori variabili, poi questi valori vengono trasmessi separatamente e non possono in alcun caso essere interpretati come codice SQL, qualunque sia il loro contenuto. Un attaccante può inserire qualsiasi stringa di caratteri, incluse istruzioni SQL complete, ma questi caratteri resteranno trattati come un semplice dato testuale senza alcun effetto sulla struttura della query.

Un esempio concreto della differenza

Una query costruita per concatenazione diretta assomiglia tipicamente a una stringa assemblata con la variabile dell'utente inserita in mezzo al testo SQL. Una query preparata con PDO separa questa logica: la query contiene un segnaposto, generalmente rappresentato da un punto interrogativo o da un nome preceduto da due punti, e il valore reale viene fornito separatamente al momento dell'esecuzione, tramite un metodo dedicato dell'oggetto di connessione. Questa differenza strutturale, una volta compresa, non richiede più righe di codice di una concatenazione manuale, contrariamente a un'idea a volte diffusa.

Cosa non coprono le query preparate

Le query preparate proteggono specificamente dall'iniezione SQL, ma non esentano dalle altre buone pratiche di sicurezza applicativa, come la convalida del formato atteso di un dato prima ancora di inviarlo al database, o l'escaping appropriato dei dati mostrati in una pagina HTML per proteggersi da un altro tipo di falla chiamata iniezione di script. Un modulo che accetta un'email deve ad esempio verificare che il dato assomigli a un'email prima dell'elaborazione, indipendentemente dalla protezione fornita dalla query preparata stessa.

Il caso degli ORM e dei framework moderni

I framework PHP comuni e le librerie ORM usano query preparate internamente per impostazione predefinita, il che protegge automaticamente lo sviluppatore finché usa i metodi forniti da questi strumenti anziché costruire manualmente una query grezza all'interno di quegli stessi framework. Su uno sviluppo interamente su misura senza framework, la responsabilità di sistematizzare le query preparate ricade interamente sulle abitudini di codifica adottate fin dall'inizio del progetto, il che rende ancora più importante farne una regola non negoziabile piuttosto che un'opzione occasionale.

Punti da verificare prima della pubblicazione: confermare che la descrizione del funzionamento di PDO e MySQLi resti esatta per le versioni di PHP attualmente in supporto attivo, e verificare che la distinzione tra iniezione SQL e iniezione di script corrisponda effettivamente a meccanismi di protezione distinti, senza confonderli nella formulazione.