Il presente articolo è stato tradotto automaticamente.

Sicurezza ASP.NET

Applicazioni ASP.NET a prova di hacker

Adam Tuliper

Quasi quotidianamente, i mass-media segnalano che un altro sito è stato violato da un hacker. Queste intrusioni costante da parte di gruppi hacker prominente lasciano gli sviluppatori chiedendosi se quei gruppi utilizza tecniche avanzate per il loro lavoro nefasto. Sebbene alcuni attacchi moderne possono essere abbastanza complessi, spesso, quelli più efficaci sono straordinariamente semplici — e sono stati in uso per anni. Per fortuna, questi attacchi sono di solito sorprendentemente facili da proteggere.

Io vado a dare un'occhiata ad alcuni dei tipi più comuni di hacking attacchi nel corso di due articoli. Questo primo articolo riguarderà SQL injection e parametro manomissione, mentre la seconda parte, che apparirà nel numero di gennaio, si concentrerà su cross-site scripting e cross-site request forgery.

Nel caso in cui ti stai chiedendo se solo siti di grandi dimensioni devono essere preoccupati per l'hacking, la risposta è semplice: Tutti gli sviluppatori devono essere preoccupati per tentativi di hack nelle loro applicazioni. È il vostro lavoro a proteggere le vostre applicazioni; gli utenti si aspettano di esso. Anche le piccole applicazioni su Internet sono aperte a sondare a causa del gran numero di programmi hacking automatici disponibile. Supponiamo che la tabella utenti o clienti è stata rubata e le password per tali tabelle sono utilizzate anche per altre applicazioni. Naturalmente, è consigliabile che gli utenti sempre utilizzare password diverse, ma la realtà è, non lo fanno. Non vuoi avere notificare agli utenti che l'applicazione è stata il gateway per rubare informazioni. Il blog molto piccolo di un amico, che coprono il suo viaggio a mt. Everest è stato hackerato e tutto cancellato per nessun motivo apparente. Senza protezione, praticamente nessuna applicazione è sicura.

A meno che la vostra rete è fisicamente scollegata da qualsiasi dispositivo di comunicazione di fuori, il potenziale esiste per qualcuno di salire alla rete tramite problemi di configurazione del proxy; Remote Desktop Protocol (RDP) o rete privata virtuale (VPN) attacca; vulnerabilità di esecuzione di codice remoto eseguite da un utente interno semplicemente visitando una pagina Web; indovinato le password; regole del firewall inadeguata; Wi-Fi (più sicurezza Wi-Fi può essere violata da un utente malintenzionato seduto nel vostro parcheggio); trucchi di ingegneria sociale che convincere la gente a dare volontariamente informazioni sensibili e altri punti di ingresso. A meno che non completamente staccato dal mondo esterno, nessun ambiente può presumere di essere completamente sicuro.

Ora che ho paura si (spero) a credere che la minaccia di hacking è molto reale e tutte le applicazioni possono essere sondate, cominciamo questi hack e come è possibile impedire la comprensione!

Infiltrazione SQL

Cos'è? SQL injection è un attacco in cui uno o più comandi vengono inseriti in una query per formare una nuova query che non fu mai inteso dallo sviluppatore. Questo si verifica quasi sempre quando viene utilizzato SQL dinamico; cioè, quando stai concatenazione delle stringhe nel codice per istruzioni SQL di forma. SQL injection può verificarsi in Microsoft.NET Framework codice se si sta formando una query o la procedura chiamata, e può accadere nel vostro lato server T-SQL codice pure, così come nel caso di SQL dinamico nelle stored procedure.

SQL injection è particolarmente pericoloso perché può essere utilizzato per eseguire query e modificare dati, ma anche per l'esecuzione di comandi di database che sono limitati solo dalla utente del database o autorizzazioni di account di servizio del database. Se SQL Server è configurato per l'esecuzione con un account di amministratore e il vostro utente dell'applicazione appartiene al ruolo sysadmin, essere particolarmente preoccupato. Attacchi utilizzando SQL injection possono eseguire i comandi del sistema per eseguire le operazioni seguenti:

  • Installare backdoor
  • Trasferire un intero database sulla porta 80
  • Installare sniffer di rete per rubare le password e altri dati sensibili
  • Rompere le password
  • Enumerare la rete interna, tra cui i porti di altre macchine di scansione
  • Scaricare file
  • Eseguire programmi
  • Eliminare i file
  • Diventare parte di una botnet
  • Completamento automatico le password memorizzate sul sistema di query
  • Creare nuovi utenti
  • Creare, eliminare e modificare dati; creare ed eliminare tabelle

Questo non è un elenco esaustivo e il pericolo è limitato solo dalle autorizzazioni in luogo — e la creatività dell'attaccante.

SQL injection è stato intorno per così tanto tempo che mi viene spesso chiesto se è ancora una preoccupazione. La risposta è assolutamente e aggressori lo uso molto spesso. Infatti, di fuori di attacchi Denial of Service (DoS), SQL injection è l'attacco più comunemente usato.

Come le è sfruttata? SQL injection è generalmente sfruttata tramite entrata diretta su una pagina Web o tramite il parametro manomissione, che di solito comporta non solo forme o gli URI che alterano — cookies, intestazioni e così via sono anche oggetto di attacco, se l'applicazione utilizza questi valori in un'istruzione di SQL insicura (tratterò questo più tardi nell'articolo).

Vediamo un esempio di SQL injection tramite il modulo anti-manomissione. Questo è uno scenario che ho visto molte volte nel codice di produzione. Il codice non può essere esattamente lo stesso, ma questo è un modo comune per gli sviluppatori di controllare le credenziali di accesso.

Ecco l'istruzione SQL in modo dinamico formato per il recupero di login dell'utente:

string loginSql = string.Format("select * from users where loginid= '{0}

  ' and password= '{1} '"", txtLoginId.Text, txtPassword.Text);

Questo costituisce l'istruzione SQL:

    select * from dbo.users where loginid='Administrator' and  
    
        password='12345'

Di per sé, questo non è un problema. Tuttavia, supponiamo che la forma campo input assomiglia ciò che viene mostrato Figura 1.

Malicious Input Instead of a Valid Username
Figura 1 Input dannoso invece di un nome utente valido

Questo input form l'istruzione SQL:

    select * from dbo.users where loginid='anything' union select top 1 *
    
      from users --' and password='12345'

Questo esempio inserisce un ID di accesso sconosciuto di "nulla", che di per sé non restituirebbe alcun record. Tuttavia, e poi i sindacati tali risultati con il primo record nel database, mentre la successiva "-" commenti fuori tutto il resto della query così viene ignorato. L'attaccante non è solo in grado di accedere, può anche ritornare record dell'utente valido al codice chiamante senza in realtà avere alcuna idea di un nome utente valido.

Il codice non può replicare questo scenario esatto, ovviamente, ma il punto importante nell'analisi delle applicazioni è di pensare a dove i valori che sono inclusi nelle query comunemente provengono, tra cui:

  • Campi modulo
  • Parametri URL
  • Valori memorizzati nel database
  • Cookies
  • Intestazioni
  • File
  • Spazio di memorizzazione isolato

Non tutti questi possono essere immediatamente evidenti. Ad esempio, perché sono intestazioni di un potenziale problema? Se l'applicazione archivia le informazioni di profilo utente nelle intestazioni e quei valori sono utilizzati nelle query dinamiche, potrebbero essere vulnerabili. Tutti questi possono essere fonti di attacco se si utilizza SQL dinamico.

Pagine Web che includono funzionalità di ricerca può essere eccezionalmente facile preda per gli attaccanti, perché essi forniscono un metodo diretto per tentare di iniezioni.

Questo può dare a un utente malintenzionato quasi una query editor funzionalità in un'applicazione vulnerabile.

Persone sono diventati molto più consapevole dei problemi di sicurezza negli ultimi anni, sistemi sono generalmente più temprati per impostazione predefinita. Per esempio, il sistema procedura xp_cmdshell è disattivato in SQL Server 2005 e istanze successive (incluso SQL Express). Non lasciate che questo darvi l'idea che un utente malintenzionato siano in non è in grado di eseguire comandi sul vostro server. Se l'account che nell'applicazione viene utilizzata per il database ha un livello abbastanza alto di autorizzazioni, un utente malintenzionato può iniettare semplicemente il seguente comando per riattivare l'opzione:

    EXECUTE SP_CONFIGURE 'xp_cmdshell', '1'

Come evitare SQL Injection? Prima discutiamo come non risolvere questo problema. Un approccio molto comune fissazione applicazioni ASP classiche era di sostituire semplicemente trattini e citazioni. Purtroppo, questo è ancora ampiamente usato in.NET applications, spesso come l'unico mezzo di protezione:

string safeSql = "select * from users where loginId = " + userInput.Replace("—-", "");

safeSql = safeSql.Replace("'","''");

safeSql = safeSql.Replace("%","");

Questo approccio si presuppone che hai:

  1. Protetto da ogni singola query correttamente con questi tipi di chiamate. Essa si basa sullo sviluppatore ricordando includere questi controlli in linea ovunque, invece di utilizzare un modello che protegge per impostazione predefinita, anche dopo la codifica che tutto il fine settimana e avendo a corto di caffeina.
  2. Digitato-controllato ogni singolo parametro. Di solito è solo una questione di tempo prima che uno sviluppatore si dimentica di controllare che un parametro da una pagina Web è, ad esempio, davvero un numero e quindi utilizza questo numero in una query per qualcosa come un ProductId senza fare che qualsiasi stringa controlla su di esso, perché, dopo tutto, è un numero. Cosa succede se un utente malintenzionato modifica ProductId e quindi è semplicemente leggere dalla stringa di query come illustrato di seguito:
URI: http://yoursite/product.aspx?productId=10

Rendimenti

select * from products where productid=10

E poi l'attaccante inietta comandi quali:

URI: http://yoursite/product.aspx?productId=10;select 1 col1 into #temp; drop table #temp;

Che produce

select * from products where productid=10;select 1 col1 into #temp; drop table #temp;

Oh no! Hai appena iniettato in un campo integer che non era filtrato attraverso una funzione di stringa o di tipo controllato. Questo è chiamato un attacco di iniezione diretta perché virgolette non sono necessarie e la porzione iniettata direttamente viene utilizzata nella query senza citare. Si può dire "sempre assicurarsi che tutti i miei dati è selezionata," ma che mette la responsabilità di controllare ogni singolo parametro manualmente su sviluppatore ed è molto aperto a errori. Perché non risolverlo il modo corretto utilizzando un modello migliore in tutta l'applicazione?

Quindi qual è il modo corretto per prevenire SQL injection? È davvero abbastanza semplice per la maggior parte degli scenari di accesso dati. La chiave è utilizzare chiamate con parametri. Si può effettivamente avere SQL dinamico che è sicuro come è effettuare tali chiamate con parametrizzate. Qui ci sono le regole di base:

  1. Affinché che utilizzi solo:
    • Stored procedure (senza SQL dinamico)
    • Con parametri di query (vedere Figura 2)

Figura 2 Query con parametri

using (SqlConnection connection = new SqlConnection(  ConfigurationManager.ConnectionStrings[1].ConnectionString))

{

  using (SqlDataAdapter adapter = new SqlDataAdapter())

  {

    // Note we use a dynamic 'like' clause

    string query = @"Select Name, Description, Keywords From Product

                   Where Name Like '%' + @ProductName + '%'

                   Order By Name Asc";

    using (SqlCommand command = new SqlCommand(query, connection))

    {

      command.Parameters.Add(new SqlParameter("@ProductName", searchText));

      // Get data

      DataSet dataSet = new DataSet();

      adapter.SelectCommand = command;

      adapter.Fill(dataSet, "ProductResults");

      // Populate the datagrid

      productResults.DataSource = dataSet.Tables[0];

      productResults.DataBind();

    }

  }

}
  • Con parametri di stored procedure richiede (vedere Figura 3)

Figura 3 con parametri chiamata di Stored Procedure

//Example Parameterized Stored Procedure Call

string searchText = txtSearch.Text.Trim();

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ConnectionString))

{

  using (SqlDataAdapter adapter = new SqlDataAdapter())

  {

    // Note: you do NOT use a query like: 

    // string query = "dbo.Proc_SearchProduct" + productName + ")";

    // Keep this parameterized and use CommandType.StoredProcedure!!
string query = "dbo.Proc_SearchProduct";

    Trace.Write(string.Format("Query is: {0}", query));

    using (SqlCommand command = new SqlCommand(query, connection))

    {

      command.Parameters.Add(new SqlParameter("@ProductName", searchText));

      command.CommandType = CommandType.StoredProcedure;

      // Get the data.
DataSet products = new DataSet();

      adapter.SelectCommand = command;

      adapter.Fill(products, "ProductResults");

      // Populate the datagrid.
productResults.DataSource = products.Tables[0];

      productResults.DataBind();

    }

  }

}
  1. 2. Dynamic SQL nelle stored procedure devono essere chiamate con parametri a sp_executesql. Evitare l'utilizzo di exec — non supporta le chiamate con parametri. Evitare di concatenazione delle stringhe con input dell'utente. Vedere Figura 4.

Figura 4 con parametri chiamare a sp_executesql

/*

This is a demo of using dynamic sql, but using a safe parameterized query

*/

DECLARE @name varchar(20)

DECLARE @sql nvarchar(500)

DECLARE @parameter nvarchar(500)

/* Build the SQL string one time.*/

SET @sql= N'SELECT * FROM Customer  WHERE FirstName Like @Name Or LastName Like @Name +''%''';

SET @parameter= N'@Name varchar(20)';

/* Execute the string with the first parameter value.
*/

SET @name = 'm%'; --ex.
mary, m%, etc.
note: -- does nothing as we would hope!
EXECUTE sp_executesql @sql, @parameter,

                      @Name = @name;

Nota a causa della mancanza di parametro sostenervi non uso: exec ' selezionare... ' + @ sql

  1. 3. Non basta sostituire trattini e citazioni e pensare che stai sicuro. Scegliere i metodi di accesso di dati coerenti come già descritto che impediscono di iniezione SQL senza intervento manuale dello sviluppatore e bastone a loro. Se si basano su una routine di escape e capita di dimenticare di chiamarlo in un'unica posizione, potrebbe essere violato. Inoltre, ci potrebbe essere una vulnerabilità nel modo in cui che si implementa la routine di escape, come può essere il caso di un attacco di troncamento di SQL.
  2. 4. Convalida dell'input (vedere la sezione seguente parametro manomissione) tramite tipo controlla e casting; utilizzare le espressioni regolari di limitare, per esempio, solo i dati alfanumerici o tirare dati importanti da fonti note; non si fidano dei dati provenienti dalla pagina Web.
  3. 5. Le autorizzazioni di oggetto di database per limitare l'ambito di applicazione utente, limitando così la superficie di attacco di audit. Concedere autorizzazioni come aggiornamento, eliminare e inserire solo se tale utente deve essere in grado di eseguire tali operazioni. Ogni applicazione separata dovrebbe avere il proprio account di accesso al database con autorizzazioni limitate. Mio open source di SQL Server le autorizzazioni revisore può aiutare con questo; si prega di check it out a sqlpermissionsaudit.codeplex.com.

È molto importante controllare le autorizzazioni di tabella se si utilizzano query con parametri. Query con parametri richiedono un utente o un ruolo disponga delle autorizzazioni per accedere a una tabella. L'applicazione può essere protetto contro SQL injection, ma cosa succede se un'altra applicazione che non è protetto tocca il database? Un utente malintenzionato potrebbe iniziare l'interrogazione via nel vostro database, quindi ti consigliamo di fare in modo che ogni applicazione dispone di un proprio accesso unico, limitato. Si dovrebbero anche consente di controllare le autorizzazioni per gli oggetti di database come viste, procedure e tabelle. Stored procedure richiedono solo le autorizzazioni per la procedura, non sul tavolo, in genere, purché non vi è nessun SQL dinamico all'interno della stored procedure, sicurezza è un po' più facile da gestire su di loro. Ancora una volta, può aiutare a mio revisore di autorizzazioni di SQL Server.

Si noti che Entity Framework utilizza una query con parametri dietro le quinte e così è protetto contro SQL injection per gli scenari di utilizzo normale. Alcuni preferiscono mappare la loro entità di stored procedure, anziché aprendo le autorizzazioni di tabella per le query con parametri dinamiche, ma ci sono argomenti validi su entrambi i lati, e che è una decisione per voi a fare. Si noti che se si utilizzano in modo esplicito Entity SQL, ti consigliamo di essere a conoscenza di alcune considerazioni di sicurezza aggiuntive per le query. Si prega di vedere la pagina MSDN Library, "Considerazioni di sicurezza (Entity Framework)," a msdn.microsoft.com/library/cc716760.

Parametro manomissione

Cos'è? Manomissione del parametro è un attacco in cui i parametri sono alterati al fine di cambiare la funzionalità previsto dell'applicazione. I parametri possono essere su una forma, stringa di query, cookies, database e così via. Tratterò gli attacchi che implicano parametri basati sul Web.

Come le è sfruttata? Un utente malintenzionato Modifica parametri per l'applicazione in esecuzione di un'azione che essa non era con l'intenzione di ingannare. Si supponga che si salva il record di un utente leggendo il suo ID utente dalla stringa di query. È sicuro? N. Un utente malintenzionato può manomettere un URL nell'applicazione in modo simile a quanto riportato nella Figura 5.

An Altered URL
Figura 5 un URL alterato

Così facendo, l'utente malintenzionato potrebbe caricare un account utente che non intendi. E troppo spesso, codice dell'applicazione simile al seguente si fida ciecamente questa userId:

// Bad practice!
string userId = Request.QueryString["userId"];

// Load user based on this ID

var user = LoadUser(userId);

**C'è un modo migliore?**Sì! È possibile leggere i valori da una fonte più attendibile come sessione di un utente o da un provider di appartenenze o profilo piuttosto che confidando la forma.

Vari strumenti rendono abbastanza facile da manomettere più appena la stringa di query. Mi consiglia di che controllare alcune delle barre i Web browser sviluppatore strumenti disponibili per visualizzare elementi nascosti sulle tue pagine. Penso che sarete sorpresi di che cosa ha scoperto e come i dati sono facile da manomettere. Si consideri la pagina "Modifica utente" mostrata Figura 6. Se si rivelano i campi nascosti nella pagina, è possibile vedere un utente ID diritto incorporato nella forma, pronto per la manomissione (vedere Figura 7). Questo campo viene utilizzato come chiave primaria per il record dell'utente, e manomissione con esso altera quali record viene salvato nel database.

An Edit User Form
Nella figura 6 un modulo utente Edit

Revealing a Hidden Field on the Form
Figura 7, rivelando un campo nascosto nella forma

**Come evitare manomissioni del parametro?**Non fidarsi dei dati forniti dall'utente e convalidare i dati su che riceverai che le decisioni si basano. Generalmente non vi preoccupate se un utente modifica il nome di medio memorizzato nel suo profilo. Tuttavia, ci si cura sicuramente se ha manomesso l'ID del form nascosto che rappresenta il tasto di registrazione utente. In casi come questo, si possono tirare dati attendibili da una fonte conosciuta sul server, piuttosto che dalla pagina Web. Questa informazione potrebbe essere archiviata in sessione dell'utente su account di accesso o il provider di appartenenze.

Ad esempio, un approccio di gran lunga migliore utilizza le informazioni dal provider di appartenenze anziché utilizzando i dati del modulo:

// Better practice

int userId = Membership.GetUser().ProviderUserKey.ToString();

// Load user based on this ID

var user = LoadUser(userId);

Ora che avete visto come inaffidabili dati dal browser può essere, diamo un'occhiata ad alcuni esempi di questi dati per aiutare le cose pulite un po ' di convalida. Qui ci sono alcuni scenari tipici di Web Form:

// 1.
No check!
Especially a problem because this productId is really numeric.
string productId = Request.QueryString["ProductId"];

// 2.
Better check

int productId = int.Parse(Request.QueryString["ProductId"]);

// 3.Even better check

int productId = int.Parse(Request.QueryString["ProductId"]);

if (!IsValidProductId(productId))

{

    throw new InvalidProductIdException(productId);

}

Figura 8 mostra un tipico scenario MVC con associazione di modello che non le conversioni di base di tipo automatico senza dover eseguire il cast in modo esplicito i parametri.

Figura 8 utilizzo di associazione del modello MVC

[HttpPost]

[ValidateAntiForgeryToken]

public ActionResult Edit([Bind(Exclude="UserId")] Order order)

{

   ...
// All properties on the order object have been automatically populated and 

   // typecast by the MVC model binder from the form to the model.
Trace.Write(order.AddressId);

   Trace.Write(order.TotalAmount);

   // We don’t want to trust the customer ID from a page

   // in case it’s tampered with.
// Get it from the profile provider or membership object

   order.UserId = Profile.UserId;

   // Or grab it from this location

   order.UserId = Membership.GetUser().ProviderUserKey.ToString();

   ...
order.Save();}

   ...
// etc.
}

Associazione del modello è una grande caratteristica di Model-View-Controller (MVC) che aiuta con parametro controlli, come le proprietà dell'oggetto dell'ordine saranno automaticamente popolate e convertite in loro tipi definiti sulla base delle informazioni di forma. È possibile definire le annotazioni di dati sul tuo modello pure per includere molte differenti convalide. Basta stare attenti a limitare quali proprietà permettono di essere popolati e, ancora una volta, non si fidano i dati della pagina per gli elementi importanti. Una buona regola è di avere un ViewModel per ogni visualizzazione, così si sarebbe completamente escludere un UserId dal modello in questo esempio di modifica.

Nota che io uso il [Bind(Exclude)] attrib­ute qui per limitare ciò che MVC è vincolante nel mio modello al fine di controllare che cosa fare o non si fidano. Questo assicura la UserId non provenienza dai dati del modulo e quindi non può essere manomessi. Associazione modello e le annotazioni di dati sono oltre la portata di questo articolo. Ho preso solo un breve sguardo qui per mostrare come parametro digitando può funzionare in MVC e Web Form.

Se è necessario includere un campo ID della pagina Web che si "fidare", si prega di visitare il link di estensioni di sicurezza MVC (mvcsecurity.codeplex.com) per un attributo assistere con questo.

Il confezionamento di

In questo articolo, ho ho presentato due dei modi più comuni applicazioni sono hacked. Come potete vedere, tuttavia, gli attacchi possono essere impediti o almeno limitati apportando modifiche a pochi nelle vostre applicazioni. Naturalmente, ci sono variazioni su questi attacchi e in altri modi, le applicazioni possono essere sfruttate. Verrà coprire due ulteriori tipi di attacchi, cross-site scripting e cross-site request forgery, nel prossimo numero.

Adam Tuliper è un progettista software con Cegedim e sviluppa software per più di 20 anni. Egli è un oratore di comunità nazionale INETA e parla regolarmente a conferenze e.Gruppi di utenti NET. Lui check out su Twitter a twitter.com/AdamTuliper, sul suo blog al completedevelopment.blogspot.com coding.com garantire la.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Barry Dorrans