Realizzare applicazioni ASP.NET sicure - Seconda parte

di Matteo Casati, in ASP.NET 2.0,

Suddiviso in due parti, questo articolo analizza le cause di errore più comuni relativamente alla sicurezza applicativa e gli accorgimenti da adottare per limitare al minimo la vulnerabilità delle applicazioni ASP.NET. Se la prima parte dell'articolo si è concentrata sugli aspetti di sicurezza legati al Cross-Site Scripting, un tipo di attacco basato sull'esecuzione di script che possono rendere molto spiacevole la navigazione dell'utente, la seconda parte dell'articolo si focalizza principalmente sugli aspetti di sicurezza legati allo strato di accesso ai dati.

Protezione da SQL Injection

SQL Injection è una tecnica che sfrutta vulnerabilità nella sicurezza dello strato di accesso ai dati di un'applicazione. Chiariamo di cosa si tratta attraverso un paio di esempi.

Esempio 1

Supponiamo di avere un semplice form di login ("txtUsername" e "txtPassword") che verifichi le credenziali di accesso di un utente mediante l'istruzione SQL così costruita:

string sql = string.Format(
  "SELECT * FROM Utenti WHERE Username='{0}' AND Password='{1}'",
  txtUsername.Text, txtPassword.Text);

e che un utente immetta i seguenti valori:

txtUsername: Administrator';--
txtPassword: (qualsiasi valore)

L'istruzione SQL risulterebbe così composta:

SELECT * FROM Utenti WHERE Username='Administrator';-- AND Password='...'

con il risultato che la condizione sul campo Password non sarebbe considerata nella clausola di WHERE in quanto preceduta dall'apertura di un commento ("--") con l'effetto di autenticare l'utente solo sulla base dello username fornito.

Esempio 2

Immaginiamo un semplice motore di ricerca, costituito da una casella di testo (ad es. "TextBox1") in cui l'utente inserisce il criterio di ricerca desiderato; la pagina, ricevuta la query di ricerca, costruisce l'istruzione SQL da inviare al database per l'estrazione dei record corrispondenti:

string sql = string.Format(
  "SELECT * FROM MyTable WHERE MyField = '{0}'",
  TextBox1.Text);

Un utente malintenzionato potrebbe digitare nella casella di testo la sequenza "a'; DROP TABLE MyTable; --", generando il seguente statement SQL:

SELECT * FROM MyTable WHERE MyField = 'a'; DROP TABLE MyTable; --'

che, una volta eseguito, comporta... la cancellazione della tabella MyTable!

Soluzione: usare le query parametriche

ADO.NET fornisce un valido sistema per proteggersi da attacchi di questo tipo: l'uso dei parametri. Il modo corretto di effettuare l'operazione presentata nell'esempio numero 2 su un database SQL Server è quindi:

SqlConnection cn = new SqlConnection("...");
string sql = "SELECT * FROM MyTable WHERE MyField = @MyValue";
SqlCommand cmd = new SqlCommand(sql, cn);
SqlParameter p = new SqlParameter("@MyValue", SqlDbType.NVarChar, 100);
p.Value = TextBox1.Text;
cmd.Parameters.Add(p);
SqlDataReader dr = cmd.ExecuteReader();

Scenari a rischio

Come abbiamo visto, l'uso di query parametriche protegge la base dati da attacchi di SQL Injection; esistono tuttavia alcuni casi in cui non è possibile utilizzare i parametri per impostare i valori oppure dove anche l'uso degli stessi non risulta sufficiente. Alcuni scenari di questo tipo sono le condizioni basate sulla clausola "IN" (esempio: SELECT * FROM MyTable WHERE Id IN (1,3,27)) e su "TOP(n)", supportato in modo parametrico solo a partire da SQL Server 2005 (esempio: SELECT TOP 10 * FROM MyTable). Inoltre rientrano tra i casi a rischio le ricerche full-text che utilizzano l'operatore "LIKE" oppure le query particolari dove la componente parametrica è costituita dal nome di una tabella o di un campo.

Questi casi e, in generale, tutte quelle situazioni in cui le istruzioni SQL sono ottenute dalla concatenazione di stringhe, richiedono una particolare attenzione per evitare l'iniezione di codice dannoso. A tale proposito due utili funzioni per rimuovere il codice potenzialmente pericoloso possono essere le seguenti:

// Per rimuovere i delimitatori di stringa
public static string SafeSqlLiteral(string sql)
{
  return sql.Replace("'", "''");
}

// Per rimuovere le wildcard per la clausola LIKE
public static string SafeSqlLikeClause(string sql)
{
  string s = sql;
  s = s.Replace("[", "[[]");
  s = s.Replace("%", "[%]");
  s = s.Replace("_", "[_]");
  return s;
}

È comunque importante notare che il semplice raddoppiamento dei carattere di quotatura singola non è sufficiente a garantire la protezione da SQL Injection. È sempre buona norma verificare l'input proveniente dall'utente in modo dettagliato, forzando quando possibile il tipo mediante cast a tipi nativi del .NET Framework come Int32, Boolean, DateTime, ecc.

Si consideri ad esempio l'istruzione:

string sql = "SELECT * FROM MyTable WHERE Id = " + SafeSqlLiteral( Request.QueryString["id"] );

Con la querystring "?id=23;DROP TABLE MyTable;--" la query risultante, nonostante l'uso della funzione SafeSqlLiteral, diventa:

SELECT * FROM MyTable WHERE Id = 23; DROP TABLE MyTable;--

Il modo corretto per trattare questo caso è forzare la conversione ad intero del valore di ID ricevuto in querystring:

int id = -1; // valore predefinito
int.TryParse(Request.QueryString["id"], out id);
string sql = string.Format("SELECT * FROM MyTable WHERE Id = {0}", id);

Come si può facilmente dedurre dagli esempi proposti, vulnerabilità di questo tipo sono molto pericolose e, data la relativa semplicità di realizzazione, anche molto frequenti: sono stati registrati numerosissimi casi di attacchi a siti Web mediante SQL Injection, anche nei confronti di enti pubblici e governativi tra cui, lo scorso agosto, il sito ufficiale delle Nazioni Unite (vedi: "United Nations VS SQL Injections" per maggiori dettagli).

4 pagine in totale: 1 2 3 4

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti