La SQL injection è uno dei principali veicoli d’attacco ai database. Evitare di scrivere direttamente il codice SQL utilizzare metodi più sicuri possono garantire la difesa rispetto questo tipo di attacchi.
Generalmente, si inviano ai database comandi SQL. In particolare il comando SELECT viene utilizzato per ottenere dei dati dal database. Queste istruzioni sono semplicemente dei testi e normalmente questo non è un problema ma quando si comincia a concatenare del testo possono sorgere diversi problemi.
In particolare ci si espone al rischio quando si costruisce l’istruzione SQL con l’input fornito dall’utente. Ad esempio si chiede l’utente un valore per filtrare dei dati e poi si crea l’istruzione SQL concatenando quanto viene scritto magari con un codice come questo:
Dim sql As String sql = "SELECT * FROM Operazioni WHERE Utente = 'Paolo' AND Titolo = '" + valoreDellUtente " + "'" |
Se l’utente scrive “Movimenti” come valore allora la stringa SQL sarà come questa:
SELECT * FROM Operazioni WHERE Utente = 'Paolo' AND Titolo = 'Movimenti' |
Ma cosa succede se l’utente scrive qualcosa di più strano come “Movimenti’ OR 1;”? La stringa SQL risulterebbe in questo caso:
SELECT * FROM Operazioni WHERE Utente = 'Paolo' AND Titolo = 'Movimenti' OR 1;' |
Queste sono due istruzioni SQL. La prima restituisce tutti i dati della tabella (a causa dell’ “OR 1”) e la seconda solo un apice e viene ignorata. Quindi l’applicazioni risponde con tutti i dati metre noi volevamo filtrarli.
Questo tipo di operazione è detta perchè l’utente è stato in grado di inserire del codice SQL nella query, e questo a causa della concatenazione.
Come evitarlo
Invece di utilizzare la concatenazione, che tra l’altro è anche complessa da scrivere in caso di query composte, è meglio utilizzare una funzionalità del database ovvero i PreparedStatement che permettono di utilizzare i parametri.
In Xojo i PreparedStatement sono disponibili per tutti i database supportati. Vediamo un esempio con un database mioDB di tipo SQLite:
//Prima creaiamo la string SQL base Dim sql As String sql = "SELECT * FROM Operazioni WHERE Utente = 'Paolo' AND Titolo = ?" //Ora creaiamo il prepared statement, collegando i tipo di valori, il valore // e inviando la richiesta al database per ottenere il recorset risultante Dim ps As SQLitePreparedStatement ps = MioDB.Prepare(sql) ps.BindType(0, SQLitePreparedStatement.SQLITE_TEXT) ps.Bind(0, valoreDellUtente) Dim rs As RecordSet rs = ps.SQLSelect |
Visto che il valore dell’utente è trattato come un parametro, se viene inserito ancora il codice “strano” questo viene interpretato come se fosse:
SELECT * FROM Operazioni WHERE Utente = 'Paolo' AND Titolo = 'Movimenti'' OR 1;' |
Questo è un codice SQL normale che probabilmente non restituirà nessuna riga.
Un’applicazione che prevede l’inserimento di dati da parte dell’utente in un comando SQL dovrebbe utilizzare sempre i preparedStatement sia perchè siamo più sicuri contro azioni pericolose (volontarie o meno), sia perchè alla fine sono più semplici e chiari da gestire nel codice ed infine anche perchè diversi database li ottimizzano ottenendo quindi un guadagno nelle performance.