Questo argomento è trattato molto spesso nel web ma poiché ho potuto vedere con i miei occhi che ancora oggi tanti siti soffrono di questo problema, ho pensato che parlarne non sia proprio tempo perso.

Prima di tutto diciamo brevemente cos’è.

La SQL injection è una tecnica dell’hacking mirata a colpire le applicazioni web che si appoggiano su un database di tipo SQL. Il metodo usato per forzare un sito sfrutta la mancata implementazione di controlli sui campi che si ricevono in input che arrivano tramite il GET o il POST al nostro sito. Se non vengono fatti i dovuti controlli, il nostro malintenzionato può inviare codice maligno all’interno di una richiesta sfruttando il codice SQL e il nostro DB. Se il nostro sito è vittima di questo attacco le conseguenza possono essere disastrose in quanto il nostro intruso può autenticarsi con privilegi anche si root e cambiare o visualizzare dati sensibili.

Come tutte le cose efficienti il metodo usato per questo attacco è molto semplice e si basa sulla possibilità di inviare del codice SQL opportunamente creato in un form o in una query string in modo tale che il server interpreti il nostro codice nel DB ma un esempio è molto più esplicativo.

Prendiamo un semplice form come questo

<form action=’login.php’ method=’post’>

Username: <input type=’text’ name=’user’ />

Password: <input type=’password’ name=’pwd’ />

<input type=’submit’ value=’Login’ />

</form>

Ora prendiamo un file in PHP (si può usare anche ASP) e facciamo un controllo per verificare se l’utente ha le credenziali corrette per accedere alla nostra pagina protetta

<?php

//Prepara la query, in una variabile

$query = “SELECT * FROM users WHERE user='”.$_POST[‘user’].”‘ AND pwd='”.$_POST[‘pwd’].”‘”;

//Esegue la query (supponiamo che sia già aperta una connessione valida al database e $db è lo stato)

$sql = mysql_query($query,$db);

//Conta il numero di righe trovate (se questo numero è maggiore di 0 i dati immessi sono corretti)

if(mysql_affected_rows($sql)>0)

{

//Esegue la convalida dell’autenticazione e permette l’accesso a pagine protette

}

?>

Un programmatore esperto sa già che questo sistema non è sicuro poiché permetterebbe a un utente di forzare l’accesso ma molti neo programmatori o semplici appassionati non sempre vedono il grande pericolo che si cela in queste poche righe…come? Vi chiederete e vi rispondo subito.

Proviamo a inserire come nome utente pippo e
questa stringa nel campo password ‘ OR ‘user’=’user e poi inviamo il form al nostro server per il controllo. Le credenziali che abbiamo inviato ovviamente non sono corrette ma riusciremo comunque ad entrare e il motivo sta nella cattiva implementazione del codice.

Quando i dati arrivano al server il risultato della variabile $query sarà

SELECT * FROM users WHERE USER=pippoAND pwd=”
OR
USER=’USER

In rosso ho evidenziato quello che è contenuto in “$_POST[‘user’]” e “$_POST[‘pwd’]” e come si può notare questa query da come risultato tutti gli utenti contenuti nella tabella users e questo implica che l’utente pippo riesce ad entrare nel sito anche se non conosce la reale password! Ma c’è di più, perché anche cambiando il nome utente in pluto il risultato non cambia visto che il vero miracolo lo fa la parte dopo OR.

In pratica il nostro bravo hacker si è limitato a inviare una stringa che una volta inserita nella query crea questo risultato per il filtro WHERE

USER=’pippo’ AND PWD=”, che danno risultato FALSE ma OR ‘USER’=’USER’ è un’identità quindi sempre TRUE come tutti sanno OR è TRUE se almeno uno dei due elementi è TRUE.

Come si è appena visto con poche righe di codice si è riusciti a forzare un normale login mal progettato. Qualcuno penserà che alla peggio si possono danneggiare i dati del sito e del DB ma tanto non sono sensibili e ci sono i backup e quindi non può creare danno gravi…e se vi dicessi che si possono inviare piccoli file in PHP nel server e avere accesso alla macchina come root??

Quante volte vi è capitato di vedere siti in cui si vede una cosa del tipo

https://www.miosito.com/elenco_ordini.php?id=4

Anche in questo caso se non sono stati fatti i dovuti controlli esponiamo il nostro sito/web-app all’attacco. Un simile URL spesso nasconde query del tipo

SELECT * FROM ordini WHERE IDOrdine=$_GET[‘id’] AND IDGruppo=3;

Ho inserito una condizione AND IDGruppo=3 per rendere meglio l’idea del grave problema di questo metodo. Immaginiamo di sostituire al valore 4 della query string la seguente stringa

    “-1 ORDER BY 5 — “

La nostra query SQL diventerà

    SELECT * FROM ordini WHERE IDOrdine=-1 ORDER BY 5 — AND IDGruppo=3;

Questa query che all’apparenza sembra innocua, da moltissime informazioni e crea un risultato che non avevamo previsto. I doppi trattini ” — ” con lo spazio finale fanno si che tutto ciò che venga dopo di essi sia considerato del DB come un commento e quindi la condizione AND IDGruppo=3 è ignorata!

La parte “-1 ORDER BY 5” farà si che il DB non trovi alcun valore nella tabella ordini (difficilmente usiamo valori negativi per gli ID) e cerchi di ordinare i risultati delle 5 colonne…un attimo…chi ha detto che nella tabella ordini ci siano 5 colonne? Nessuno e se dovessero essere minori allora basterà ridurre a 4 o 3 oppure aumentare a 6 o 7 per vedere se ci sono più colonne. Ma a cosa serve sapere il numero di colonne? Serve per la prossima query la più devastante per la nostra sicurezza! Supponiamo che la tabella abbia 5 colonne e inviamo questa nuova query

    https://www.miosito.com/elenco_ordini.php?id=-1 union all select 1,2,3,4,5 —

SELECT * FROM ordini WHERE IDOrdine=-1 UNION ALL SELECT 1,2,3,4,5 — AND IDGruppo=3;

Con questa query stiamo dicendo al DB di unire al risultato della regolare query il risultato di una seconda select con che stampa a video 1 2 3 4 5 (per chi non conosce il comando UNION ALL lo invito a consultare il WEB). Questa query per essere eseguita necessita che il numero di campi della prima select sia uguale al numero della seconda…ecco spiegato a cosa serviva il comando order by di prima!

Fatto questo il nostro hacker può fare tutto quello che vuole sapere la versione del DB sostituendo uno dei numeri, esempio il 3, con version() oppure con una sotto query come (SELECT ‘ciao’). Quello che spesso molti ignorano e che i DB come MySQL sono settati con permessi molto alti e possono sia leggere file che scrivere, non ci credete?

Allora provate su un vostro DB questa query

    SELECT ‘ciao’ INTO
OUTFILE ‘/tmp/ciao.txt’

E ora immaginate se venisse scritto un file php che permette di avere accesso ai file e al sistema con una shell che ha I diritti di root…adesso iniziate a capire quanto danno può fare no.

Il metodo è semplice e serve solo sostituire a ‘ciao’ una stringa in esadecimale che in realtà è il nostro file PHP

Ora che abbiamo visto i danni che si possono creare, dobbiamo anche sapere come evitare questi attacchi di SQL injection. Per prima cosa dobbiamo dire che non esiste una soluzione assoluta, che sia sicura e portabile allo stesso tempo. Il punto fondamentale è saper gestire l’input dell’utente. A seconda dei dati che si trattano, è possibile adottare più sistemi per aumentare la sicurezza

  • Controlli sul tipo di dato
  • Creazione di filtri tramite espressioni regolari
  • Escape di caratteri potenzialmente dannosi

PHP per esempio mette a disposizione delle funzioni per eseguire queste operazioni, per esempio se ci aspettiamo che un campo ID proveniente da una query string sia un intero, allora è buona cosa fare un casting della variabile in questo modo

    $id = (int) $_GET[‘id’];

Il PHP non definisce a priori se una variabile deve essere intero o stringa ma in questo modo $id deve essere preso come intero. Se in $_GET[‘id’] ci fosse una stringa $id sarebbe uguale a zero! Abbiamo evitato che possano essere passate istruzioni SQL!

Spesso i dati che si aspettano in input possono essere descritti da una espressione regolare. Supponiamo che lo username di un utente registrato possa essere una stringa alfanumerica composta da un minimo di 4 ed un massimo di 12 caratteri. Per filtrare dati che non rispettano questi vincoli è possibile utilizzare la funzione preg_match()

if (preg_match(“/^[a-z0-9]{4,12}$/i”, $login)) {

// le condizioni sono rispettate e si può procedere con il login

}

else {

// errore

}

PHP mette a disposizione anche dei validi strumenti per evitare che caratteri potenzialmente pericoli possano essere inviati al DB (ricordiamo ‘ e “) tramite la funzione mysql_real_escape_string()

$voce = “Questi sono carettare potenzialmente pericolosi ‘ e ” “;

echo $voce;

$voce_con_escape = mysql_real_escape_string($voce);

echo $voce_con_escape;

Questi sono alcuni metodi per evitare che malintenzionati possano danneggiare il nostro lavoro tramite SQL injection ma ovviamente bisogna sempre tenere la guardia alta e pensare sempre che non esiste la sicurezza al 100%!

Proprio per questo motivo vi lascio con una serie di luoghi comuni e ditemi se non conoscete almeno un “cugino” o “amico” che non abbia detto qualcosa del genere

“il mio sito è piccolo, chi verrà mai a cercarmi?” oppure “è solo una intranet , non è esposta e quindi non sto rischiando nulla”.

Uno dei miti da sfatare è quello dell’hacker, nella stanza buia mentre sferra attacchi informatici dietro la sua amata tastiera. Il primo errore l’ho fatto io stesso associando la parola hacker con l’immagine del delinquente; hacker è solo chi ha fame di conoscenza e così come un coltello, la conoscenza si può sfruttare nel bene o nel male.L’oscura figura che porta gli attacchi inizia il suo lavoro tramite tools automatizzati che cercano in modo indiscriminato su tutti gli indirizzi internet un PC che soffra di vulnerabilità note. L’assenza di distanza fisica e di conoscenza dell’importanza o meno del sito rende vulnerabili tutti a pari merito, siano essi utenti in dialup o superserver con informazioni supersegrete.

L’altro mito da sfatare è che nella intranet non si corrano rischi.

Le statistiche dicono il contrario e la intranet è fonte di attacchi portati a compimento da utenti a volte consapevoli ed altre volte inconsapevoli. Questo è un buon motivo per pensare alla sicurezza anche in ambiente intranet, non dimenticando mai che è anche un obbligo di legge nel nostro paese. Oltre alla protezione da SQL Injection ci sono una lunga serie di buone pratiche come l’adozione di credenziali che abbiano i minimi privilegi necessari, la politica del “least privilege”.

Con questo si conclude questo articolo riguardo gli attacchi ai siti. Vi invito a postare altri metodi di attacco e di difesa!

Tengo a precisare che tutte le informazioni che sono scritte qui sono a puro scopo informativo e di prevenzione e per nessun motivo devono essere usate per eseguire attacchi. Declino ogni responsabilità!