La SQL injection (abbreviato "SQLi") è attacco mirato a colpire le applicazioni web che si appoggiano su un DBMS di tipo SQL. Questo attacco sfrutta l'inefficienza dei controlli sui dati ricevuti in input ed inserisce codice maligno all'interno di una query SQL. Le conseguenze prodotte sono imprevedibili per il programmatore: l'SQL injection permette al malintenzionato di autenticarsi con ampi privilegi in aree protette del sito anche senza essere in possesso delle credenziali d'accesso e di visualizzare e/o alterare dati presenti del database.
Applicazione pratica
Per un esempio pratico ricorreremo ad uno script in PHP che si appoggia ad un database MySQL. La tecnica che è alla base dell'SQL injection è comunque identica anche per altri tipi di database o di linguaggio (come l'ASP). Lo script utilizzato come esempio si occupa di autenticare un utente ed è diviso in due file: il primo è form.html (un semplice form per il login in HTML), il secondo login.php (è l'elaborazione che controllerà i dati e stabilirà, se consentito, il login, in questo caso in PHP). L'utente visualizza form.html e compila i dati, che verranno automaticamente inviati a login.php, che li memorizza sotto forma di variabile globale $_POST.
form.html
Il form è molto semplice: ha solo due campi, uno per l'username e uno per la password. I dati immessi verranno poi passati (come detto) a login.php, nelle variabili rispettive $_POST['user'] e $_POST['pwd']. Una volta ricevuti questi dati, PHP effettua una query e li cerca all'interno del database. Se verranno trovati procederà all'autenticazione dell'utente.
login.php
L'attacco di SQL injection sta proprio nell'iniettare nello script PHP dati arbitrari tramite il form in HTML. In questo caso, se lo script non compie i dovuti controlli, basta immettere per esempio come user pippo e come password ' OR user='pippo per accedere con le credenziali dell'utente pippo (ipotizzando l'esistenza dell'utente di nome pippo). La query per il database diventerà infatti:
La disgiunzione inclusiva OR è uguale al legame logico VEL e restituisce TRUE se una delle due condizioni è vera. La condizione per l'utente pippo è verificata e quindi il login viene effettuato.
Proteggersi dalla SQL injection
La protezione deve essere progettata in fase di sviluppo del programma assicurandosi che l'input rispetti certe regole:
- controllare il tipo di dato ricevuto, eventualmente forzandolo mediante casting (se ad esempio ci si aspetta un valore numerico, controllare che l'input sia un valore numerico oppure si può forzare affinché diventi comunque un valore numerico);
- filtrare i dati ricevuti attraverso espressioni regolari;
- sostituire i caratteri potenzialmente pericolosi con equivalenti caratteri innocui (ad esempio per applicazioni WEB codificandoli con gli equivalenti codici HTML);
- effettuare l'escape dei dati ricevuti (ogni linguaggio, solitamente, mette a disposizione particolari strumenti per questo scopo, ad esempio mysqli_real_escape_string, prepare statement and store procedures in PHP, e PreparedStatement in Java).
- criptare le credenziali di accesso prima di inserirle nella query SQL (evitare che le informazioni sensibili siano memorizzate nel DB in chiaro).
Ovviamente, questi metodi possono essere applicati anche insieme sullo stesso dato in input. La scelta varia proprio a seconda delle tipologie di questi dati. Occorre, quindi, prestare particolare attenzione a tutte le varianti di un input, tenendo conto di ogni possibile (oppure improbabile) ipotesi.