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
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.
Il modo più immediato è lanciare la seguente query
' having 1=1--ovvero generare una query del tipo
select * from miatabella where userId='' having 1=1provate 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
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 utentil'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
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.
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 ...
E' buona regola:
Ma il problema più grave è che probabilmente questa è solo una parte delle possibilità di attacco.
Torna alla home page di SQL Server