Home Page Home Page Articoli SQL Injection, che cosa è e come difendersi

SQL Injection, che cosa è e come difendersi

I malintenzionati sono sempre dietro l'angolo. Il SQL Injection è una delle pratiche più semplici da utilizzare per attaccare un'applicazione poco sicura e violarla o arrecare danni al database. Vediamo che cos'è e come garantire la sicurezza delle applicazioni in modo adeguato.
Autore: Alessandro Alpi Livello:

Introduzione


Capita ancora troppo spesso che gli utenti di molti forum/newsgroup chiedano o propongano soluzioni che non sono sicure. Alcuni propongono concatenazione di stringhe SQL direttamente dall'applicazione, altri chiedono come fare per creare Dynamic SQL (SQL Dinamico, in alcuni casi pericoloso).

Che cos'è il SQL Injection


Che cos’è il SQL Injection? È la tecnica per cui un utente malintenzionato utilizza la parte di input per creare SQL dinamico per poter accedere al database. La pericolosità dell'attacco poi aumenta se la connessione al database si ha senza definizione accurata di un livello di privilegi. Molte applicazioni infatti tendono ancora ad accedere alla base dati tramite la classica e famosa combinazione user/pass sa/sa o utenti amministratori. Se la nostra applicazione permette SQL Injection la sicurezza è del tutto compromessa e i danni potenziali sono infiniti.

Scenario tipico


Immaginiamo di avere una semplice applicazione che effettua ricerche su una lista di utenti, costituita da una sola pagina .aspx con due TextBox ed un Button che invia i dati al server.
Inoltre avremo un database Utili con all'interno una tabella così definita e popolata:

Codice SQL n°1
CREATE TABLE dbo.Utenti
(
IDUtente int IDENTITY(1,1) NOT NULL
, Nome varchar(30) NOT NULL
, Cognome varchar(30) NOT NULL
, CONSTRAINT PK_dboUtenti PRIMARY KEY CLUSTERED
(
IDUtente
)
)
GO

INSERT INTO dbo.Utenti (Nome, Cognome)
VALUES ('Alessandro', 'Alpi')
INSERT INTO dbo.Utenti (Nome, Cognome)
VALUES ('Marco', 'Rossi')
INSERT INTO dbo.Utenti (Nome, Cognome)
VALUES ('Michael', 'Denny')
INSERT INTO dbo.Utenti (Nome, Cognome)
VALUES ('Daniele', 'Zanella')
INSERT INTO dbo.Utenti (Nome, Cognome)
VALUES ('Manuele', 'Carra')
GO


La pagina è la seguente:

Codice .NET n°2
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtNome" runat="server" />


<asp:Button ID="btnCercaProtetto" runat="server" Text="Ricerca Protetta" OnClick="btnCercaProtetto_Click" />
<asp:Button ID="btnCercaInjection" runat="server" Text="SQL Injection" OnClick="btnCercaInjection_Click" />
<asp:GridView ID="GWRisultati" runat="server" AutoGenerateColumns="true">
</asp:GridView>
</div>
</form>
</body>


La ConnectionString definita per connetterci al nostro SQL Server 2005 prevede accesso tramite l'utente sa (pratica da evitare assolutamente, ma utile per l’esempio).
Ipotizziamo inoltre di utilizzare un metodo scritto da noi per effettuare la connessione e la ricerca sulla tabella sopra definita:

Codice .NET n°3
private void BindRicerca(String strNome, String strConn)
{
DataTable dt = new DataTable("Risultati");
SqlConnection oConn = new SqlConnection(strConn);
SqlCommand oCmd = new SqlCommand("SELECT * FROM dbo.Utenti WHERE Nome = '" + strNome + "'", oConn);

SqlDataAdapter da = new SqlDataAdapter(oCmd);
da.Fill(dt);

GWRisultati.DataSource = dt;
GWRisultati.DataBind();
}


Come avviene un SQL Injection Attack


Eseguendo una ricerca utilizzando la nostra TextBox txtNome avremo il risultato sperato, ovvero una griglia con tante righe quante sono quelle che hanno il campo nome che eguaglia il nostro input.

La query restituita dalla concatenazione tra il nostro codice e l’input sarà simile a questa:

SELECT * FROM dbo.Utenti WHERE Nome = 'Alessandro'

Ma con una semplice modifica, un utente malintenzionato, anche se non particolarmente "skillato" in materia di database, può effettuare una richiesta pericolosissima. Immaginiamo di inserire nella TextBox il valore "x' OR 'a'='a", ecco la SELECT risultante:

SELECT * FROM dbo.Utenti WHERE Nome = 'x' or 'a'='a'

Come si vede subito la query risponderà sempre con tutto l'elenco dei record della tabella, in quanto 'a'='a' è una condizione sempre vera.



Questo è un primo esempio, molto semplice ma allo stesso tempo molto pericoloso. Pensate solo ad una query che deve controllare la login di un utente durante la fase di autenticazione. Normalmente la query si aspetta come risultato una sola riga. A questo punto concatenando statement SQL opportunamente seguendo l'esempio precedente è possibile avere accesso ad un sito Internet o ad un'area riservata amministrativa senza conoscerne la password. Questo esempio non viene mostrato perchè l'articolo non è un Tutorial o una guida per diventare Hacker ma è a scopo didattico.

Ricordiamo inoltre che la ConnectionString è basata su "sa" e quindi un sysadmin, con il massimo delle permission. Proviamo ora a mettere questa la stringa come Input:

"x’; DROP TABLE dbo.Utenti; --"

nella textbox. Eseguiamo la prima ricerca senza ottenere nulla e appena lanciamo la seconda ?

N.B. Ricordiamo che tramite l'uso del punto e virgola (;) è possibile concatenare più statement SQL da passare in un'unica volta a SQL Server. Mentre l'uso del doppio meno (--) consente di commentare/remmare ciò che viene dopo nella stringa di comandi, quindi ignora completamente eventuali statement SQL successivi.



L'errore, che probabilmente gli sviluppatori ed i DBA non avevano previsto è drammatico. La tabella dbo.Utenti non esiste più. È vero che per trovare il nome della tabella, bisogna fare alcuni tentativi (o usare altri metodi di SQL Injection). Però una volta arrivati lì, il gioco è fatto ed è grave che un utente che naviga possa arrivare fino a quel punto. Fortunatamente esistono tanti rimedi.

Come difendersi dal SQL Injection


Come fare? L’oggetto è andato perduto. Se esiste un piano di Backup, il restore ci permette di ricaricare lo stato del database all'ultimo backup effettuato. Ma tutto quello che intercorre tra quest'ultimo e l'attacco dell'utente malintenzionato è andato perso completamente.
Quindi la vera risposta è pensare a come difendersi la prossima volta. E le soluzioni ci sono:

1) Stored Procedures


Col passaggio di parametri si riduce di molto l'attacco di malintenzionati, ma è necessario richiamare le Stored Procedures sfruttando la collection Parameters dell'oggetto SqlCommand e non tramite codice SQL (CommandType = Text). Nel secondo caso infatti è possibile passare input non filtrato per effettuare con semplicità un attacco SQL Injection.
La Stored Procedure potrebbe essere simile a questa:

Codice SQL n°4
CREATE PROCEDURE dbo.proc_GetUsers
(
@Nome varchar(20)
)
AS
BEGIN
SET NOCOUNT ON;

SELECT *
FROM dbo.Utenti
WHERE Nome = @Nome
END


La chiamata da codice sarà questa:

Codice .NET n°5
private void BindRicercaSP(String strNome, String strConn)
{
DataTable dt = new DataTable("Risultati");
SqlConnection oConn = new SqlConnection(strConn);

// nome Stored Procedure
SqlCommand oCmd = new SqlCommand("dbo.proc_GetUsers", oConn);
// Tipo comando Stored Procedure
oCmd.CommandType = CommandType.StoredProcedure;

SqlDataAdapter da = new SqlDataAdapter(oCmd);

// collection Parameters
oCmd.Parameters.Add("@Nome", SqlDbType.VarChar, 20).Value = strNome;

Response.Write(oCmd.CommandText);
da.Fill(dt);

GWRisultati.DataSource = dt;
GWRisultati.DataBind();
}


2) Query parametriche


Allo stesso modo l'attacco viene ridotto se si utilizzano le Query Parametriche. Indicare parametri con i PlaceHolder "@nomeparametro" ed utilizzare la collection Parameters dell'oggetto SqlCommand è necessario per ridurre la superficie di attacco.
Ecco come modificare il nostro codice per le Query Parametriche:

Codice .NET n°6
private void BindRicerca(String strNome, String strConn)
{
DataTable dt = new DataTable("Risultati");
SqlConnection oConn = new SqlConnection(strConn);

// parametro indicato con @NomeParametro
SqlCommand oCmd = new SqlCommand("SELECT * FROM dbo.Utenti WHERE Nome = @Nome", oConn);
SqlDataAdapter da = new SqlDataAdapter(oCmd);

// collection Parameters
oCmd.Parameters.Add("@Nome", SqlDbType.VarChar, 20).Value = strNome;

Response.Write(oCmd.CommandText);
da.Fill(dt);

GWRisultati.DataSource = dt;
GWRisultati.DataBind();
}


3) Utilizzo degli oggetti forniti da Visual Studio e dal Framework


Alcuni oggetti, come il SQLDataSource o l'AccessDataSource, forniscono un buon livello di protezione da attacchi di tipo SQL Injection. Quindi, appena possibile, si consiglia il loro utilizzo con la definizione dei Parameters:

Codice .NET n°7
<asp:SqlDataSource ID="SQLDS1" runat="server" SelectCommand="SELECT * FROM dbo.Utenti WHERE Nome = @Nome" ConnectionString="<%$ ConnectionStrings:UtiliConnectionString %>">
<SelectParameters>
<asp:ControlParameter ControlID="txtNome" Name="Nome" />
</SelectParameters>
</asp:SqlDataSource>


4) Validazione dell'Input


La gestione della sicurezza di un'applicazione e del rischio di eventuali danni dall'esterno non è una cosa che si risolve rapidamente come per accendere una luce, sicurezza on/sicurezza off, è una cosa graduale che si raggiunge man mano nel lifecycle della costruzione di un'applicazione e bisognerebbe tenerne conto sin dall'inizio quando si disegna o progetta l'applicazione stessa.

La validazione dell'Input dell'utente riveste un ruolo di fondamentale importanza, perchè bloccando già lì eventuali tentativi d'attacco, si interrompe subito la catena che può portare danni nei livelli inferiori.

Per esempio è sempre buona norma nelle applicazioni Web ASP.NET e non applicare la proprietà MaxLength alle TextBox e quindi limitare il numero di caratteri. In questo modo eventuali query maligne o malformate non riuscirebbero a passare perchè troncate alla sorgente.

Utilizzare i Page Validators è un'altra tecnica molto utile. Se abbiamo una TextBox che accetta in Input un CAP quindi numerico, mi aspetto che il mio validatore Javascript verifichi che nella TextBox ci siano 5 numeri nè più nè meno.

Qualcuno potrebbe semplicemente disabilitare il Javascript e bypassare la validazione Client-side. Fortunatamente via codice ASP.NET si può comunque richiedere una validazione di tutti i validatori della Webform richiamando il Page.IsValid e verificare prima di procedere oltre se è tutto ok.

5) Livello di sicurezza ed autorizzazione su database


Progettare bene il livello di sicurezza e di accesso degli utenti al database è una pratica fondamentale che chi progetta l'architettura del database e la sua security dovrebbe tenere sempre a mente.

Come abbiamo visto esistono vari modi per evitare il SQL Injection.
Vorrei ripetere che è del tutto fondamentale la progettazione di un livello di sicurezza direttamente sulla base dati, con tanto di definizione di utenti e ruoli e con diversi livelli di autorizzazione/profilazione.
Molti di noi trovano che quest'ultima parte sia "inutile", ma al contrario è fondamentale per la protezione delle proprie applicazioni. E' un po come per i backup, è certamente inutile fare il backup, perchè richiede tempo e voglia, ma tutta l'utilità viene recuperata istantaneamente nel momento in cui perdiamo tutti o parte dei dati.

Il caso del Virus da SQL Injection


Un'errata programmazione delle applicazioni sul fronte della security ha consentito per esempio al virus o pseudo-virus di cui parlo adesso di creare ingenti danni a livello planetario. Il funzionamento del virus è molto banale, sfrutta la SQL Injection e fa delle chiamate HTTP o POST alle pagine modificando i valori dei parametri e inserendo un blocco di codice T-SQL opportunamente modificato e codificato in Base64 che una volta in esecuzione enumera tutte le colonne di tipo text,varchar,char e omologhi Unicode e va ad appendere al testo di ogni campo uno script Javascript che una volta renderizzato nella pagina Web a sua volta dovrebbe collegarsi in remoto e installare un ulteriore virus sulla macchina locale o comunque qualcosa di maligno che probabilmente aumenta la diffusione del fenomeno stesso su scala mondiale. Qui trovate un post su dove provengono gli attacchi a livello geografico:
Analisi geografica degli IP del virus da SQL Injection Attacks (Blog MVP David De Giacomi) 

Il Virus in sè non causa gravi danni, se non il fatto che va a "sporcare" il database e nel caso dei campi text fa un CAST a varchar(4000) quindi tronca il testo e quindi vi fa perdere parte del contenuto del campo. Quindi in questo caso bisogna avere un backup a disposizione per recuperare i dati perduti.

Il meccanismo del Virus è ben spiegato in questo Bullettin:
SANS SQL Injection: More of the same 

Tools per combattere il Virus SQL Injection su SQL Server


Dopo che il danno è stato fatto e non si riesce in breve tempo a fixare tutti i problemi e le falle di sicurezza bisogna cercare rapidamente di arginare il problema, ma in che modo ?
Per difendersi da questo pseudo-virus anche Microsoft ha dovuto rapidamente e/o aggiornare dei tools per i suoi clienti colpiti ripetutamente da questi attacchi.

Ripulitura del Database SQL Server
L'esecuzione di questo Virus "sporca" il database, riempiendo tutti i campi text,varchar,char e omologhi Unicode, con del codice HTML. La prima cosa da fare è restorare un backup recente oppure eseguire una ripulitura manuale del database ove possibile. La ripulitura può essere fatta riga per riga (se poche sono le righe/tabelle infettate) oppure semi-automatica, sfruttando la stessa tecnica del virus ma al contrario, con l'uso dei cursori, del Dynamic SQL e enumerando le colonne e le tabelle usando le tabelle di sistema (syscolumns e sysobjects) facendo dei REPLACE. Se non siete capaci di eseguire un'operazione del genere meglio farla fare ad altri perchè potreste creare più danni di quelli che già ci sono.

URLScan Filter 3.0 Beta
Questo tool consente di filtrare, limitare e bloccare certi tipi di HTTP Request che arrivano su IIS. Per installarlo e utilizzarlo è necessario avere il controllo del server su cui è ospitata la nostra soluzione. Nel caso di spazi shared hosting ciò non è possibile a meno chè il vostro Hoster provveda ad installarlo e a configurarlo per voi.

Download URL Scan Filter3.0 Beta 
Istruzione per l'uso di URL Scan Filter 

Microsoft Source Code Analyzer for SQL Injection
Un Tool per analizzare pagine .asp statiche e rilevare eventuali punti critici e vulnerabilità che possono compromettere la sicurezza e la stabilità dell'applicazione web. Stranamente pare che non analizzi pagine ASP.NET ma solo le vecchie ASP 3.0. Richiede installato sulla macchina il .NET Framework 3.0.

Se ne parla anche sul mio blog e su quello del collega MVP Lorenzo Benaglia:
Prova di Source Code Analyzer per SQL Injection (Blog MVP Alessandro Alpi) 
Source Code Analyzer for SQL Injection (Blog MVP Lorenzo Benaglia) 

Download URL Scan Filter3.0 Beta 
Istruzione per l'uso di Source Code Analyzer for SQL Injection 

Costruzione di un HttpModule che filtri le richieste HTTP
Se non avete accesso a IIS potete sempre costruirvi un HttpModule personalizzato in .NET che fa praticamente la stessa cosa dell'URLScan Filter 3.0 e lo potete deployare facilmente nella root della vostra applicazione web e abilitare. In questo modo eventuali richieste non conformi possono essere bloccate.

Costruzione di un HttpModule che blocchi a livello di IP
In questo caso questa tecnica non dà nessun risultato significativo, perchè il virus si distribuisce su vari client (attacco distribuito) nel mondo quindi gli attacchi possono provenire da blocchi di IP appartenenti a nazioni diverse, quindi si rischierebbe di tagliare fuori dal proprio sito anche ignari utenti che appartengono a un certo IP netblock bloccato. C'è da dire però che a seguito di un'analisi fatta sugli IP che effettuano gli attacchi si può desumere facilmente che questi provengono da stati non industrializzati ma emergenti dove probabilmente non c'è nessun tipo di legislazione in merito.

Qui trovate una dimostrazione del perchè una soluzione basata sul blocco degli IP potrebbe non essere efficiente per questo tipo di minaccia:
Analisi geografica degli IP del virus da SQL Injection Attacks (Blog MVP David De Giacomi) 

Link Utili sulla sicurezza delle applicazioni e sul SQL Injection


Per finire vorrei indicare alcuni link utili a mio avviso da leggere se non si ha bene a mente come si scrive codice sicuro e quindi non violabile:

Creazione di codice di accesso ai dati protetto 
Injection Protection 
Preventing SQL Injections in ASP 
How To: Protect From SQL Injection in ASP.NET 
Coding techniques for protecting against Sql injection 
Filtering SQL injection from Classic ASP 
SQL Injection Attack 

Un articolo del collega italiano MVP Raffaele Rialdi:
Una piaga chiamata SQL Injection 

E per finire ancora da un collega MVP Erland Sommarskog un trattato sul Dynamic SQL molto interessante:
The Curse and Blessings of Dynamic SQL 

Disclaimer


Tutte le tecniche mostrate in questo articolo sono a solo scopo didattico e tese a sensibilizzare Developers, WebMasters e DBA al tema della Security e a migliorare la loro conoscenza in questo ambito. Gli attacchi portati a termine dal Virus o manualmente da voi comportano comunque un danno per chi li riceve e nonostante il fatto che questi attacchi possano essere messi a segno su applicazioni non sicure o dove c'è qualche falla aperta a causa della scarsa competenza o disattenzione di chi le ha sviluppate, ciò non toglie che qualcuno potrebbe richiedervi un risarcimento danni, quindi prestate attenzione alle vostre azioni e magari evitate di ritrovarvi a dover risarcire il danno con gli sviluppatori incauti che hanno sviluppato l'applicazione. ;-)
Voto medio articolo: 4.8 Numero Voti: 12
Alessandro Alpi

Alessandro Alpi

Il mio avvicinamento all'informatica si ha nel 1992 all'età di 11 anni, grazie al mio stupendo C64 ed il suo basic. Dopo essermi diplomato in informatica, inizio subito il lavoro da sviluppatore in una ditta di Parma. Passo 6 anni qui, lavorando per i primi 6 mesi su prodotti win32 (per lo più gestionali per ... Profilo completo

Articoli collegati

Utilizzare NUnit per testare codice .NET
Scopriamo in questo articolo come utilizzare il popolare framework Open Source NUnit per effettuare Unit Testing del codice .NET aumentandone la qualità e riducendo il numero di eventuali bugs.
Autore: Michela Zangarelli | Difficoltà:
Visual Studio 2005 Team System
Scopriamo il nuovo IDE di sviluppo che copre interamente l'intero ciclo di vita del software integrando in un solo ambiente più prodotti e studiato apposta per i ruoli di Architect, Developer e Tester.
Autore: Marco Caruso | Difficoltà: | Voto:
Tutorial ADO.NET ed esempi pratici - Parte 2
Dopo aver fatto un po' di teoria su ADO.NET nella Parte 1 dell'articolo procediamo con un Tutorial pratico che ci condurrà alla creazione di un'applicazione basilare che consenta l'accesso e la modifica dei dati su un Database Access.
Autore: Stefano Passatordi | Difficoltà: | Commenti: 10
Introduzione ad ADO.NET - Parte 1
ADO.NET è uno dei componenti chiave del .NET Framework. Eredita il nome dal vecchio ADO ma è praticamente quasi tutto cambiato. Una nuova architettura, nuovi concetti, nuove funzionalità e nuovi oggetti. Vediamo quali sono e a cosa servono.
Autore: Stefano Passatordi | Difficoltà: | Commenti: 6
Colonne calcolate e parola "Child" con ADO.NET
Una cosa utile di ADO.NET e' la possibilita di aggiungere ad una colonna da codice il cui valore sia il risultato di operazioni sui dati dei record (o tra valori di altre colonne). Quando si utilizza un dataset con piu tabelle collegate mediante relazioni invece, usando la parola chiave "child" si possono avere delle informazioni riguardanti le tabelle figlio, direttamente nei record della tabella
Autore: Matteo Raumer | Difficoltà: | Voto:
Usare degli indicatori di progresso con Query SQL
Nell'articolo vediamo come sia possibile con un l'aiuto delle classi DataReader e Command di ADO.NET, mostrare un indicatore di progresso che indichi lo stato di avanzamento di una query SQL durante il fetching dei dati.
Autore: Matteo Raumer | Difficoltà: | Voto:
La crittografia e la classe Rijndael
Vediamo come utilizzare una delle tante classi messe a disposizione dal .NET Framework per la crittografia dei dati. In particolare la classe Rijndael implementa un algoritmo di crittografia molto forte diventato famoso negli ultimi anni.
Autore: David De Giacomi | Difficoltà:
DES il famoso standard creato da IBM per la crittografia dei dati
In questo articolo dimostreremo come crittografare dei file usando il .NET Framework e in particolare la classe DESCryptoServiceProvider che implementa l'ormai noto algoritmo di cifratura inventato da IBM negli anni 70 chiamato DES (Data Encryption Standard)
Autore: David De Giacomi | Difficoltà: | Commenti: 2 | Voto:
Cosa sono e come funzionano le funzioni ricorsive?
Ecco tre esempi efficaci che vi spiegano come usare correttamente le funzioni ricorsive, per effettuare ricerche di file e cartelle all'interno del disco fisso, per ricostruire la struttura di un file XML oppure per svuotare determinati controlli in una Form.
Autore: Matteo Raumer | Difficoltà: | Commenti: 2
Costruire Console Applications con Visual Studio .NET
Spiegheremo in questo articolo i passi base fondamentali per costruire Console Applications utilizzando Visual Studio .NET.
Autore: David De Giacomi | Difficoltà: | Voto:
TextReader e TextWriter
Una panoramica su come utilizzare queste due classi che ci permettono di leggere e scrivere file di testo.
Autore: David De Giacomi | Difficoltà: | Commenti: 1 | Voto:
Giochiamo un po' con il registro di Windows!
Scopriamo uno degli elementi fondamentali del sistema operativo Windows e vediamo come è possibile accedervi tramite le classi offerte dal .NET Framework: Microsoft.Win32.Registry e Microsoft.Win32.RegistryKey
Autore: David De Giacomi | Difficoltà: | Commenti: 1 | Voto:
.NET Framework 1.1 Beta
Una prima panormaica sulle novità offerte dal Framework 1.1 che sarà integrato nella prossima versione di Visual Studio .NET 2003.
Autore: David De Giacomi | Difficoltà:
Cosa posso costruire con Visual Studio .NET ?
Un' introduzione sui vari tipi di progetto disponibili in Visual Studio dalle tipiche applicazioni Windows fino alle recenti applicazioni Web e agli innovativi Web Services.
Autore: David De Giacomi | Difficoltà: | Commenti: 1
Copyright © dotNetHell.it 2002-2014
Running on Windows Server 2008 R2 Enterprise, SQL Server 2008 & ASP.NET 3.5