Si sente spesso parlare di SQL Injection come qualcosa di molto pericoloso. In sintesi estrema una SQL Injection è un tentativo di violazione della base di dati SQL che sta dietro ad un’applicazione web.
La vulnerabilità è di fatto nativa. La base di dati di un web pubblico ed accessibile a tutti deve necessariamente essere accessibile ad utenti indefiniti i quali devono essere in grado di eseguire query direttamente al database.
Per assurdo la base di dati più sicura è quella inaccessibile ad utenti indefiniti, ma nel caso di siti internet pubblici tale principio è impraticabile.
Partiamo adesso dal preludio. Esattamente cosa c’è di pericoloso? E che conseguenze può avere una vulnerabilità? Per rispondere a queste domande dobbiamo prima di tutto capire come funziona una web application che gira dietro un database SQL.
Cosa vuol dire "pagina dinamica".
Una pagina costruita dinamicamente è il risultato di uno script scritto in uno dei tanti linguaggi server-side disponibili, che da come risultato codice web istantaneo interpretabile dal browser. La pagina web che risulta dallo script nei fatti non esiste sul server, ma viene costruita ad arte.
Non necessariamente una pagina web dinamica deve includere una query SQL. Un esempio banale potrebbe essere il seguente.
<?php
//costruisco il file index.php
include ‘header.php’;
include ‘body.php’;
include ‘footer.php’
?>
Nel banale esempio riportato sopra costruisco il file index.php includendo i tre file di inclusione, ognuno dei quali conterrà proprio codice. Il file index.php fisicamente esiste, ma non contiene la reale risultanza del web browser, bensì un linguaggio di scripting, adeguatamente interpretato dal web server che passerà l’elaborazione al browser del client.
In definitiva per pagina dinamica intenderemo contenuti web creati istantaneamente, su richiesta dell’utente finale. Il contenuto delle pagine può cambiare in funzione di diversi parametri, siano essi espliciti o impliciti.
I parametri per la definizione del contenuto
Evitando di entrare troppo nel tecnicismo, facciamo subito un esempio che ci permette di capire come un parametro definito in una URL possa determinare il contenuto di una pagina web.
http://www.example.com/news.php?idnews=45
Cosa ci fa pensare la query string sopra riportata? Che visualizzeremo una news ed in particolare quella che ha come id il numero 45.
Ragionando a ritroso possiamo immaginare che esista un database con una tabella in cui vengono salvate tutte le news, identificate univocamente dal campo idnews (quasi certamente un campo di tiop AUTOINCREMENT). E possiamo anche facilmente intuire che azionando il file news.php al suo interno faremo partire una query molto simile alla seguente.
SELECT * FROM news WHERE idnews = 45
Il 45 è passato dalla URL allo script e viene incluso nella query inviata direttamente al database. Ed è qui che sta il grande pericolo! Perchè l’utente può facilmente manipolare il parametro ed includere codice pericoloso direttamente in una query. Come? Facilissimo! Basta avere conoscenze di base del linguaggio SQL.
Una semplice SQL Injection
Adesso vediamo un esempio pratico di come è possibile manipolare una URL per estrarre a nostro piacimento dei dati da un database.
http://www.example.com/news.php?idnews=45%20OR%20bozza=TRUE
In questo caso sto supponendo che nella tabella esista anche il campo bozza, che impostato in TRUE mi ritorni gli articoli non pubblicati. Con la query string posso estrarre tutti i record in bozza. Brutta cosa se il senso degli articoli in bozza è quello di non essere ancora pubblici 🙂
La cosa non vale solo per le URL, ma anche per i form ed il passaggio di variabile via POST. Un form di identificazione molto spesso fa partire una query simile a questa:
SELECT * FROM tabellautenti WHERE username=’usermane’ AND password=’password’;
Se si ottiene una riga dalla query allora ci siamo identificati correttamente. Ma se non controlliamo le variabili passate è facilissimo identificarci anche senza conoscere username e password.
SELECT * FROM tabellautenti WHERE username = ” OR 1=1 –‘ AND password = ‘password’;
Non specifichiamo nessuna username, ma inseriamo nel campo ‘ OR 1=1 –‘. In SQL i due trattini equivalgono ad un commento, quindi tutto quello che viene dopo sarà ignorato, mentre la condizione di WHERE sarà sempre verificata.
La visione a priori
Cosa bisogna fare per evitare questo tipo di inconvenienti? Prima di tutto progettare degli escape. Quali caratteri all’interno di una query ne possono determinare una modifica? I caratteri ‘, ", –, ma anche %00 se l’applicazione gira dietro un database ACCESS devono essere sempre attenzionati.
Si impone una verifica della presenza di tali elementi di vulnerabilità. Spesso i programmi deputati allo scripting hanno delle funzioni native per prevenire le injection, ma non è detto che questo basti, soprattutto perchè le vulnerabilità, seppur nascenti da specifiche della sintassi SQL, derivano al 90% da come scriviamo codice.
Prevenire ogni eventualità
In base al dato che l’utente, tramite URL o POST, passerà al web server, dobbiamo sempre immaginare a come questo potrebbe essere modificato, senza dimenticare come lo vogliamo!
Frase infelice ed un po’ criptica, ma che spiega quello che dobbiamo fare. Se selezioniamo la news tramite il campo ID della tabella, settato come AUTOINCREMENT, allora sicuramente sapremo che:
- Vogliamo un numero.
- Tale numero deve essere un integer.
La prima cosa che faremo è controllare se la variabile passata rispetta queste condizioni. Se non passa il controllo di integrità dobbiamo impostare un’azione positiva! Ad esempio inviando un header con codice 404 (file not found).
Se il parametro è obbligatorio, controlliamo innanzitutto che ci sia. Ed anche in questo caso impostiamo una risposta senzata nel caso in cui l’utente ometta la variabile o la dichiari NULL.
In generale dobbiamo ricordarci che prima di lanciare una query al database, tutte le variabili devono essere controllate accuratamente, evitando che corrispondano ad un type non ammesso e potenzialmente pericoloso.
Messaggi di Errore? No grazie!
Altro punto che da sempre considero fondamentale è quello di mascherare i messaggi di errore. Molto spesso infatti si tende ad attivarli tutti, dandogli il massimo del dettaglio. Ottima cosa in fase di sviluppo dell’applicazione, ma del tutto inadeguata per il prodotto finale.
Capita spesso infatti che i programmi di scripting diano troppe informazioni relative all’errore, mostrando in chiaro parti di query, con tanto di nomi di campi.
Cerchiamo invece di scrivere codice con le giuste funzioni di controllo, in modo da non far andare mai il web server in errore. E la cosa la si può realizzare solo controllando preventivamente (a priori 😉 ) l’integrità delle variabili passate dal client, necessarie per creare la pagina richiesta.
Conclusioni
Son cose semplici, scritte in maniera abbastanza basilare. I puristi storceranno giustamente il naso per come definisco certi aspetti della programmazione web. Ma è evidente che ormai un po’ tutti si sentono programmatori. E troppo spesso basta poco per bucare siti internet anche di rilevanza istituzionale 🙂