L’SQL injection è una delle tecniche hacking utilizzate per l’inserimento e l’esecuzione di codice SQL non previsto all’interno di applicazioni web basate su database.
Ciò che rende l’SQL injection particolarmente pericoloso è che non richiede l’uso di strumenti particolari, ma solo di un PC e di un qualsiasi browser tra quelli comunemente adoperati per la navigazione sul web.
Ogni tipologia di architettura software adoperata per la realizzazione di siti web interfacciati con un database è dunque potenzialmente vulnerabile ad un attacco SQL injection. Infatti, tale rischio non è legato all’utilizzo di un particolare linguaggio di programmazione web based (PHP, Java ecc.) né tantomeno a quello di un particolare server DB (Oracle, Mysql, SQL ecc.).
Ciò non toglie, comunque, l’eventualità che un tipo di attacco SQL injection possa essere più efficace su di un determinato sistema piuttosto che su di un altro, ma solo eventualmente per fattori che riguardano strettamente la sintassi specifica di quel linguaggio adoperato.
Bisogna prendere atto che una programmazione poca attenta alla sicurezza può accrescere in modo significativo le vulnerabilità mettendo in serio pericolo le logiche di elaborazione che costituiscono le transazioni SQL.
Indice degli argomenti
SQL injection: ecco come funziona
Per capire in cosa consista questa tecnica di attacco, occorre fare una piccola premessa su come funziona la comunicazione tra un server erogatore di servizi, in cui risiede una banca dati (DB), ed un client che tramite opportune interrogazioni usufruisce di quei servizi.
Come già accennato, sebbene esistano diversi tipi di sistemi per la gestione di database relazionali, tutti in ogni caso sono potenzialmente vulnerabili all’SQL injection. In questo articolo ci riferiremo al software di gestione database MySQL, ed al linguaggio di programmazione PHP per lo sviluppo delle applicazioni lato server.
Schematizziamo il meccanismo di interrogazione e dialogo tra client, server e DB, ipotizzando l’accesso di utente al suo profilo personale per l’erogazione di un servizio:
- il visitatore digita l’indirizzo della pagina di login che risiede su di un server (sito web);
- il server web elabora la pagina in locale e produce, come risultato, una pagina in HTML inviandola al browser del client, che si occuperà di visualizzarla sullo schermo del visitatore.
Per l’erogazione del servizio, pertanto:
- il visitatore inserirà le proprie credenziali sul form di login;
- il form di login invierà i dati al server web che tramite l’esecuzione di uno script PHP, interrogherà il proprio DB (MySQL);
- il DB solo in caso di corrispondenza delle credenziali con quelle archiviate, risponderà al server con i dati richiesti;
- il server web provvederà in locale ad impaginare opportunamente la risposta e ad inviarli al browser del client;
- il visitatore, come risultato di questo processo visualizzerà una pagina con le informazioni relative al servizio richiesto.
Porte di accesso e obiettivi d’attacco
In un contesto del genere è fondamentale, a livello di programmazione, adottare almeno delle misure minime di protezione al fine di scongiurare lo sfruttamento di ogni eventuale vulnerabilità che possa celarsi nel meccanismo di dialogo tra client, server e DB.
Particolare attenzione deve essere data alla messa in sicurezza del codice deputato alla gestione di moduli di autenticazione e pagine di ricerca che in genere sono implementati prevedendo un’archiviazione e un’interazione con un database e che rappresentano le potenziali porte di accesso.
Un criminale esperto di sintassi SQL, infatti, può inviare particolari istruzioni attraverso le pagine del sito che prevedano un dialogo con il DB, con l’obiettivo di ottenere un accesso non autorizzato all’applicazione stessa, per recuperare informazioni, dati sensibili e modificarli o eliminarli.
SQL injection: le fasi preliminari per un attacco
È consuetudine che il primo passo di un attaccante sia quello di capire quando e come l’applicazione interagisce con un server DB per accedere ad alcuni dati. Le osservazioni preliminari sono fondamentali per la ricerca di informazioni necessarie a risalire alla struttura dell’applicazione bersaglio.
Gli elementi indicatori che l’applicazione comunichi con un DB possono essere:
- moduli web: quando l’autenticazione viene eseguita utilizzando un form, è probabile che le credenziali dell’utente siano verificate rispetto a un database che archivia queste informazioni;
- motori di ricerca: la stringa per la ricerca inviata dall’utente può essere utilizzata in una query SQL che estrae da un database i record rispondenti alla richiesta.
- siti di commercio elettronico: le informazioni dei prodotti e delle loro caratteristiche sono molto probabilmente archiviate in un database.
Il passo successivo, per l’indagine, può essere quello di interpretare i messaggi di errore che un applicativo restituisce in caso di inserimenti anomali. Qualora ogni tipo di messaggio di feedback sia stato disabilitato o occultato appositamente dagli sviluppatori, è comunque possibile provare a capire il comportamento del server DB per il tramite di strategie più accurate come le tecniche di sniffing per l’intercettazione del traffico di rete.
Anatomia di un attacco SQL Injection
Un attacco SQL injection prevede, ad esempio, l’inserimento di termini specializzati nei campi di un modulo web in modo da inviare al database comandi inconsueti e imprevisti sfruttando l’applicazione stessa.
Come esempio, mostrato ad uso esclusivamente didattico, consideriamo il caso, molto semplice ma esplicativo, in cui una query interroga il DB affinché vengano restituiti delle informazioni relative al nominativo utente, inviato tramite il metodo POST di un form HTML (ricordiamo che il problema è del tutto indipendente dal tipo di piattaforma utilizzata).
Per l’esempio che segue si è preso spunto dalla pagina OSWAP dedicata al testing SQL injection.
La query Mysql è la seguente:
Se, come visto, non c’è alcuna forma di validazione dell’input, un potenziale attaccante potrebbe provare ad inserire le seguenti username e password:
$_POST[“nominativo”]= 1 ‘OR’ 1 ‘=’ 1
$_POST[“password”]= 1 ‘OR’ 1 ‘=’ 1
La combinazione d’uso delle virgolette con l’operatore logico “OR” genera il seguente comando SQL modificato ed inviato al DB:
È possibile, inoltre, includendo dei delimitatori di commenti (/*…*/), provare a far ignorare addirittura alcune parti delle query, come ad esempio quella relativa al campo password.
- è possibile carpire le credenziali amministrative di un DB, in quanto solitamente il primo utente della lista è proprio l’account admin;
- l’hashing delle password è sempre utile per garantire la riservatezza delle stesse, ma non sempre è sufficiente a garantirne l’autenticazione, in quanto come visto l’accesso tramite parola chiave può essere bypassato;
- trovati i punti di accesso ed individuate le vulnerabilità, l’utente malintenzionato può proseguire con attacchi più pesanti di iniezione SQL, come la manipolazione degli archivi digitali e/o l’esecuzione di routine personalizzate.
SQL injection: come difendersi
Per prevenire l’iniezione di query arbitrarie su quelle applicazioni web che interagiscono con un DB è sicuramente basilare, in fase implementativa, una programmazione che preveda un controllo di tutte le potenziali porte di accesso all’archivio di gestione dei dati, quali i form, le pagine di ricerca e qualsiasi altro modulo che preveda una interrogazione SQL.
La validazione degli input, le query parametrizzate tramite template ed una adeguata gestione del reporting degli errori possono rappresentare delle buone pratiche di programmazione utili allo scopo. Ecco alcuni accorgimenti:
- prestare attenzione all’utilizzo degli elementi di codice SQL potenzialmente a rischio (virgolette singole e parentesi) che potrebbero essere integrati con opportuni caratteri di controllo e sfruttati per usi non autorizzati;
- usare l’estensione MySQLi;
- disattivare sui siti la visibilità delle pagine degli errori. Spesso tali informazioni si rivelano preziose per l’attaccante, il quale può risalire all’identità e alla struttura dei server DB interagenti con l’applicazione bersaglio.
L’estensione di MySql
Una codifica accurata può ridurre sensibilmente la vulnerabilità di un’applicazione web all’iniezione arbitraria di codice SQL. Una buona soluzione risulta quella di utilizzare tra le librerie messe a disposizione da PHP per l’interazione con il MySQL, l’estensione MySQLi (MySQL improveded).
Mysqli, come suggerito dal nome, apporta delle migliorie al Mysql in particolare mettendo a disposizione due approcci di programmazione:
- procedurale (uso di tradizionali funzioni);
- orientato agli oggetti (uso di classi e metodi).
Seguono un esempio di validazione dell’input ed uno di query parametrizzate utilizzando il paradigma OOP (interfaccia ad oggetti) e facendo riferimento alle versioni PHP 5/7.
La validazione dell’input
Per sanificare caratteri speciali e di controllo potenzialmente pericolosi e presenti in una stringa da utilizzare in un’istruzione SQL è possibile usare il metodo mysqli::real_escape_string.
Qualora si abbia necessità di igienizzare particolari caratteri non gestiti dal metodo si può comunque intervenire tramite operazioni di replace manuale. In figura si riporta uno script commentato ed il relativo risultato su standard output.
L’algoritmo implememtato dimostra e verifica come l’uso della funzione di escape eviti che una frase logica (‘1’ OR ‘1’ = ‘1’) possa rendere ambigua un’istruzione SQL. Nella fattispecie la variabile $phrase grazie alla operazione di sanificazione (anteponendo un backslash su ogni carattere speciale) viene inserita senza alcun equivoco/errore nella tabella prova.
In PHP si utilizza la funzione bin2hex(). Secondo questa prassi l’istruzione in esadecimale, prima di essere eseguita, viene riconvertita in una stringa tramite la funzione unhex() di Mysql. Così facendo la sequenza di caratteri riconvertita, non essendo intrepretata come comando, non rappresenta più un potenziale pericolo.
Le Query parametrizzate
Un’altra tecnica di difesa da usare per attenuare le conseguenze di un SQL injection sono le query parametrizzate.
Con questo tipo di approccio il programmatore prima di passare i parametri alla query deve definire la sintassi delle istruzioni, distinguendo il codice dai dati indipendentemente dall’input ricevuto. In tal modo si assicura che non sia possibile alterare la finalità di una query attraverso interventi malevoli esterni.
Nel nostro esempio, se l’attaccante inserisse come username la stringa “‘1’ OR ‘1’ = ‘1’”, la query adeguatamente parametrizzata risulterebbe immune all’inganno perché l’interrogazione cercherebbe un nome utente corrispondente lettera per lettera alla stessa stringa.
Tale format prevede tre passaggi principali (si trascurano le parti di connessione, controlli, creazione dell’oggetto $mysqli e chiusura connessione al DB):
- la preparazione: viene creato un template di istruzione SQL (si utilizzano dei parametri segnaposto ? “ placeholder” per indicare i valori dei dati da specificare) ed inviato al database tramite il metodo prepare().
$mysqli->prepare(“SELECT * FROM login WHERE username = ? AND password = ?”);
- l’analisi: il template dell’istruzione sql ricevuto dal db viene analizzato, compilato e memorizzato senza eseguirlo tramite il metodo bind(). Vengono associati le variabili con i segnaposti ed il tipo di dato atteso, rispettando rigorosamente l’ordine di posizione.
$mysqli->bind_param(‘ss’,$username,$password);
- In questo caso le variabili $username e $password attesi devono essere delle stringhe ‘ss’. Altri tipi previsti sono:
- i: tipo dato numero intero;
- d: tipo dato numeri double;
- b: tipo di dato binario;
- l’esecuzione: l’applicazione, una volta associati i valori ai parametri, esegue l’istruzione tramite il metodo execute().
$result = $mysqli->execute();
Dopo aver valutato l’esito della variabile $result è possibile proseguire con il resto del codice per eventuali operazioni di inserimento, modifica o cancellazione dei record interessati.
La disattivazione della segnalazione degli errori
La segnalazione e la visualizzazione degli errori sono operazioni utilissime, per ovvi motivi, in fase di sviluppo: si rivelano una reale fonte di pericolo per la sicurezza delle applicazioni e d’incertezza e confusione per gli utenti in fase di produzione.
Si capisce bene come nelle fasi d’indagine le funzioni di reporting possano fornire, ad un utente esperto e con cattive intenzioni, preziose informazioni e indizi circa i meccanismi di funzionamento e l’architettura stessa di un’applicazione web based.
È possibile intervenire in vari modi. Nel caso del linguaggio PHP (le indicazioni riportate potrebbero cambiare in base alla versione PHP di utilizzo):
- settando opportunamente le impostazioni dei file di configurazione (i principali file di configurazione che possono essere personalizzati secondo i permessi consentiti dall’hosting provider in cui risiede il sito in produzione sono php.ini, .htaccess, httpd.conf):
- nel file php.ini potrebbe essere utile settare la variabile dispaly_error da 1 a 0 e la variabile error_reporting da 1 a 0.
- nel file .htaccess potrebbe essere utile settare la variabile php_value_error_reporting da 1 a 0.
- allestendo, tramite le funzioni standard ini_set() e error_reporting(), un file php ad hoc da includere sugli header delle pagine interessate, con la funzione include(“file_errors_ad_hoc.php”):
Restano sempre valide le buone regole:
- di aggiornare i tools, in uso per la produzione e la gestione, con le ultime release e patch che i fornitori legittimi mettono periodicamente a disposizione;
- di adottare una corretta politica per la gestione dei privilegi SQL per le utenze, prestando particolare attenzione alla custodia e robustezza delle password e disattivando gli account amministrativi convenzionali utilizzandone altri opportunamente creati al bisogno.