SQL injection: proteggere il vostro database da questa tecnica

Nelle aziende medio-grandi i database sono imprescindibili, infatti banche e assicurazioni li utilizzano per salvare e gestire le informazioni sui conti e sui pagamenti, che sono quindi memorizzati in questi pratici sistemi di archiviazione. Molte aziende pianificano le loro risorse appoggiandosi a soluzioni ERP che non funzionano senza database, senza pensare che la maggior parte dei contenuti nel World Wide Web non sarebbe realizzabile senza affidarsi a dei database. Ma per configurare e gestire questo catalogo elettronico è richiesto un grande impegno e in questo caso la sfida maggiore consiste nel garantire la sua sicurezza.

Risulta ugualmente importante impostare dei backup e usare degli hardware stabili, oltre che prendere delle misure di protezione per difendersi da accessi provenienti dall’esterno. Soprattutto le SQL injections rappresentano un grosso pericolo per i tradizionali database relazionali e le informazioni implementate.

Che cos’è una SQL injection?

Con SQL injection si intende lo sfruttamento di una vulnerabilità nei database relazionali, che utilizzano il linguaggio SQL per l’inserimento dei dati. In questo caso l’hacker approfitta di quegli input degli utenti nell’interfaccia del database che non sono filtrati correttamente e in cui sono presenti dei metacaratteri quali trattino doppio, virgolette, elementi QUOTE e punto e virgola. Questi caratteri possiedono delle funzioni particolari per l’interprete SQL e consentono la modifica esterna dei comandi eseguiti. Spesso una SQL injection si presenta correlata a dei programmi in PHP e ASP che dispongono di vecchie interfacce. Qui, a volte, gli input non sono filtrati nel modo giusto e rappresentano perciò il target perfetto per un attacco hacker.

Con un uso mirato di caratteri di funzione, un utente non autorizzato inietta in questo modo altri comandi SQL e manipola i record in modo da poterli modificare, leggere o eliminare. Nei casi più gravi è persino possibile che, sfruttando questo metodo, l’hacker riesca ad avere accesso alla riga di comando del sistema e giunga così all’intero database server.

Esempi di SQL injection: come funzionano gli attacchi al database

Visto che i database vulnerabili vengono rilevati in fretta e gli attacchi SQL injection vengono eseguiti altrettanto facilmente, questo metodo rientra tra i preferiti su scala mondiale. Così i cyber criminali agiscono mettendo in atto diversi schemi di attacco e sfruttano a proprio vantaggio le vulnerabilità recenti delle applicazioni coinvolte nel processo di gestione dei dati, prendendo di mira soprattutto quelle conosciute da tempo. Per spiegare meglio come funziona esattamente una SQL injection, abbiamo raccolto per voi alcuni dei metodi più frequenti.

Esempio 1: accesso tramite un input utente filtrato erroneamente

Solitamente, per poter accedere a un database, un utente deve prima di tutto autentificarsi. A questo scopo esistono script che, ad esempio, presentano un modulo di login, comprensivo di nome utente e password. L’utente compila il modulo e lo script effettua contestualmente i controlli per sapere se nel database esiste il record corrispondente. Di default viene creata nel database una tabella con il nome “users” e le righe “username” e “password”. In un’applicazione web qualsiasi le corrispondenti righe degli script (pseudocodice) per l’accesso al web server potrebbero essere le seguenti:

uname = request.POST['username']
passwd = request.POST['password']

sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"

database.execute(sql)

Un hacker ha ora la possibilità di manipolare il campo della password tramite SQL injection, inserendo, per esempio, password' OR 1='1, che genera la seguente richiesta SQL:

sql = "SELECT id FROM users WHERE username='' AND password='password' OR 1='1'

In questo modo ottiene l’accesso all’intera tabella utente del database, perché l’input della password è sempre vero (1='1'). Se si logga come amministratore, può fare tutte le modifiche che vuole ai record. In alternativa si può manipolare allo stesso modo il campo del nome utente.

Esempio 2: rilevare i dati tramite manipolazione ID

Interrogare le informazioni di un database tramite ID è un metodo pratico e utilizzato oggigiorno, che rappresenta però anche una porta d’accesso per una SQL injection. Così un web server sa, ad esempio tramite un inserimento ID trasmesso nell’URL, quali informazioni debba richiamare dal database. Lo script PHP corrispondente sarebbe:

<?php 
  #Datenbankabfrage anhand einer ID 
  $id = $_REQUEST['id'];
  $result = mysql_query("SELECT * from tabelle WHERE id=$id");
  
  # Anzeige des Ergebnisse ...
   ?>

Ci si aspetta che L’URL abbia la forma di …/script.php?id=22. Nel caso rappresentato il record della tabella sarebbe stato richiamato con l’ID 22. Se un utente non autentificato ha ora la possibilità di manipolare questo URL e inoltra, invece, al web server la richiesta …/script.php?id=22+or+1=1, ne deriva che tutti i dati risultati dalla mysql_query possano essere letti, e non solo il record con l’ID 22:

SELECT * FROM tabelle WHERE id=22 or 1=1

Come gli hacker scoprono database vulnerabili

In linea di massima ogni sito e applicazione web rischia di subire una SQL injection, a patto che SQL sia il linguaggio utilizzato per il database; infatti troppo spesso gli sviluppatori dei programmi che comunicano con il database non si occupano a sufficienza dell’aspetto sicurezza. Le vulnerabilità scoperte non rimangono a lungo segrete nel vasto World Wide Web ed esistono perciò pagine che informano sulle vulnerabilità attuali e svelano anche ai cyber criminali come possono trovare i siti affetti da queste vulnerabilità facendo una ricerca su Google.

Grazie ai messaggi di errore standard viene verificato in fretta se i risultati dati possano diventare un potenziale target per gli hacker. Così si può semplicemente aggiungere un apostrofo ad un URL con un parametro ID (come nell’esempio seguente):

[Domainname].de/news.php?id=5‘

Un sito attaccabile risponde con un messaggio di errore che suona più o meno così:

“Query failed: You have an error in your SQL Syntax …“

In italiano: “Richiesta fallita: la sintassi SQL inserita è errata…”. Con metodi simili si possono anche ottenere il numero delle colonne, il nome delle tabelle e delle colonne, la versione SQL o persino i nomi utente e le password. Con strumenti appositi, per la maggior parte open source, si può anche automatizzare la richiesta e la correlata SQL injection. Comunque lo sfruttamento delle vulnerabilità conosciute è perseguibile penalmente, a meno che non si tratti di propri database di cui si voglia fare un controllo sulle misure di sicurezza intraprese.

Come proteggere il vostro database da una SQL injection

Potete mettere in atto diverse misure per evitare attacchi di SQL injection sul vostro database. Per fare ciò dovrete occuparvi di tutti i componenti coinvolti, cioè del server, delle singole applicazioni e del Database Management System (sistema di gestione di basi di dati).

Primo passaggio: controllare gli input automatici delle applicazioni

Verificate e filtrate i metodi e i parametri che utilizzano le applicazioni collegate nell’inserire gli input nel database. I dati dovrebbero sempre essere presenti nei classici tipi di file per un database. Se viene richiesto un parametro numerico, potete ad esempio verificarlo con uno script PHP comprensivo della funzione is_numeric(). Nel caso del filtraggio significa che verranno ignorati i relativi caratteri speciali. Un altro punto importante è quello di fare in modo che le applicazioni non mostrino messaggi di errore esterni, che svelino informazioni sul sistema utilizzato o sulle strutture del database.

Nel frattempo sono ormai diventati comuni i cosiddetti prepared statements, che si possono utilizzare con molti DBMS. Queste istruzioni predefinite servivano originariamente per eseguire interrogazioni fatte frequentemente e, per via della loro struttura, riducono anche il rischio di una SQL injection. Infatti le istruzioni parametrizzate comunicano al database il comando SQL vero e proprio separato dai parametri. Solo lo stesso DBMS riunisce infine entrambi e filtra così automaticamente i caratteri speciali decisivi.

Secondo passaggio: occuparsi di una protezione del server completa

La sicurezza del server, sul quale eseguite il vostro DBMS, ricopre ovviamente un ruolo importante nella prevenzione contro le SQL injections. Prima di tutto va rafforzato il sistema operativo in base allo schema conosciuto:

  • Installate o attivate solo applicazioni e servizi che sono importanti per il funzionamento del database.
  • Eliminate tutti gli account utente che non vi servono.
  • Assicuratevi che tutti gli aggiornamenti importanti del sistema e dei programmi siano installati.

Maggiori sono i requisiti, che sono collegati alla sicurezza del vostro sito, più dovreste prendere in considerazione l’uso di Intrusion Detection Systems (IDS) o di Intrusion Prevention Systems (IPS), che lavorano con diversi sistemi di riconoscimento per individuare tempestivamente gli attacchi sul server, emettere i relativi avvisi e anche attivare automaticamente, nel caso degli IPS, le giuste contromisure. Può rivelarsi una misura preventiva utile anche un application-level gateway, un componente dei firewall che controlla il traffico dati tra le applicazioni e il browser direttamente sul livello di applicazione. 

Terzo passaggio: rafforzare il database e utilizzare codici sicuri

Come il vostro sistema operativo, il database dovrebbe essere ripulito di tutti quei fattori irrilevanti e aggiornato regolarmente. A questo scopo, eliminate tutte le procedure memorizzate che non vi servono e disattivate tutti i servizi inutili e gli account utente. Impostate un account database speciale, da utilizzare solo per l’accesso dal web e dotato del minimo dei permessi. Inoltre si consiglia di salvare tutti i dati sensibili nel vostro database, come ad esempio le password, in forma crittografata.

Per quanto riguarda i prepared statements, è caldamente consigliato di non utilizzare il modulo PHP mysql e di scegliere invece mysqli o PDO (PHP Data Objects). In questo modo potete proteggervi ulteriormente anche con un codice sicuro. Così ad esempio la funzione mysqli_real_escape_string() negli script PHP impedisce che i caratteri speciali vengano trasmessi al database SQL nella forma originaria e li filtra. Se ad esempio ampliate le seguenti righe di codice

$query = "SELECT * FROM users 
WHERE username= '" . $_POST['username'] . "' 
AND password= '" . $_POST['password'] . "'";

con la funzione apposita

$query = "SELECT * FROM users 
WHERE username= '" . mysqli_real_escape_string($_POST['username']) . "' 
AND password= '" . mysql_real_escape_string($_POST['password']) . "'";

i caratteri problematici degli input utente vengono sostituiti con la variante sicura SQL (\').

Un esempio di SQL injection spiegato con la storia di Bobby Tables

Sulla pagina bobby-tables.com viene affrontata l’interessante tematica degli input utente del database inserita nel fumetto web xkcd. Il fumetto mostra una madre che riceve una chiamata dalla scuola di suo figlio, amorevolmente chiamato Little Bobby Tables. Risponde di sì alla domanda se suo figlio si chiami realmente Robert'); DROP TABLE Students;–- e scopre infine che il vero motivo della chiamata è quello di comunicarle che il tentativo di creare un record per Robert nel database della scuola, ha portato all’eliminazione completa di tutti i dati del figlio sul loro database. La madre di Robert si mostra tutt’altro che apprensiva e si augura, invece, che la scuola abbia imparato dall’errore e che pulisca in futuro gli input nel database.

Il fumetto serve quindi a chiarire quali conseguenze fatali potrebbe avere l’inserimento non verificato di input degli utenti in un database. Nel caso del database della scuola la questione si è probabilmente verificata nel seguente modo: i nomi degli studenti vengono salvati in una tabella con il nomeStudents”. Non appena arriva un nuovo studente a scuola, viene inserito in questa tabella, dove il codice corrispondente sarebbe:

$sql = "INSERT INTO Students (Name) VALUES ('" . $studentName . "');";

execute_sql($sql);

Si tratta qui di un classico comando SQL INSERT, che aggiunge il contenuto delle variabili $studentName nella tabella Students. Tramite la seconda parte del codice viene trasmessa l’istruzione al database (execute_sql). Per uno studente di nome Paul l’istruzione SQL funziona come desiderata. Il codice corrispondente

$sql = "INSERT INTO Students (Name) VALUES ('" . $studentName . "');";
execute_sql($sql);

implementa Paul nella tabella Students. Ora con la stessa istruzione dovrebbe essere creato il record per Bobby Tables, cosa che comporta la seguente riga di codice:

INSERT INTO Students (Name) VALUES ('Paul');

implementa Paul nella tabella Students. Ora con la stessa istruzione dovrebbe essere creato il record per Bobby Tables, cosa che comporta la seguente riga di codice:

INSERT INTO Students (Name) VALUES ('Robert'); DROP TABLE Students;––');

In questo modo il record di Robert è stato aggiunto alla tabella, ma l’istruzione data con DROP TABLE, contenuta nel nome dello studente, ha fatto in modo che l’intera tabella sia stata infine cancellata. Per via dell’input non filtrato nel database, la SQL Injection della madre, che si cela dietro al nome del piccolo Bobby Tables, è andata a buon fine.

La soluzione proposta nel fumetto di ripulire manualmente il codice, non è in realtà consigliata. Visto che filtrare manualmente i caratteri può provocare in verità molti errori, per proteggere il vostro database da attacchi insidiosi ed evitare una SQL injection, dovreste invece preferire le soluzioni presentate prima, come istruzioni parametrizzate o funzioni di filtraggio come mysqli_real_escape_string().


Un momento! Prima di lasciarci scopri
la nostra offerta per il rientro dalle ferie:
dominio .it a 1 € per il primo anno!

Primo anno a 1 € IVA escl.,
poi 10 €/anno IVA escl.

Inserisci il dominio desiderato nella barra di ricerca per verificarne la disponibilità.