Prevenzione di XSS in ASP.NET

Molti problemi di sicurezza dei siti Web derivano dal fidarsi troppo degli utenti. La maggior parte degli utenti della tua applicazione web farà solo ciò che hanno bisogno di fare, un utente curioso o malintenzionato spesso vuole spingere i margini di accesso. A quei bordi, i buchi di sicurezza appaiono spesso nell'applicazione. Ho scritto di prevenire due tipi comuni di vulnerabilità, SQL Injection e Cross Site Request Forgery, nelle app ASP.NET precedenti. Questo articolo esamina la prevenzione di Cross Site Scripting, un terzo tipo comune di vulnerabilità nei siti Web.

Mentre un framework moderno fa molto per rendere questi attacchi più difficili, credo che dovremmo prima capire come funziona un'app è vulnerabile a un attacco. Per prima cosa, esaminiamo cos'è Cross Scripting e come può essere sfruttato.

Che cosa è Cross Site Scripting

Cross Site Scripting (spesso abbreviato in XSS) consente l'inserimento di script dannosi in un sito Web altrimenti affidabile. Questa iniezione avviene senza che l'utente ne sia a conoscenza. Lo script iniettato viene eseguito come se provenisse dal sito Web originale. Lo script dannoso può quindi accedere a qualsiasi risorsa del sito Web ospitato a cui l'utente avrebbe accesso, come ad esempio cookie o token di sessione.

L'apertura di un attacco di cross site scripting arriva quando un'applicazione web visualizza input da utenti o risorse esterne senza validarli o codificarli correttamente. Nella maggior parte degli attacchi di cross site scripting, l'utente malintenzionato tenta di inserire JavaScript nella pagina web di un server attendibile. L'autore dell'attacco può anche tentare di iniettare HTML, Flash o qualsiasi altra cosa il browser eseguirà. Indipendentemente dalla sceneggiatura, rimane l'obiettivo di far eseguire al browser il codice della scelta dell'attaccante.

Un attacco XSS persistente

Esistono tre categorie di attacchi di cross site scripting, divisi per il metodo di iniezione e il metodo di prevenzione dell'attacco. Nel primo tipo di attacco, lo script è permanentemente memorizzato sul server di destinazione e viene quindi chiamato un attacco di scripting cross site persistente. Questo attacco tenta di incorporare lo script dannoso in qualcosa come un post sul forum memorizzato in un database o in un campo apparentemente benigno come la home page di un utente del database. Con lo script persistente, ogni visitatore del sito che visualizza il post, messaggio o oggetto altrimenti compromesso, diventa una potenziale vittima dell'attacco.

Gli aggressori che tentano questo tipo di attacco in genere prendono di mira campi di commento, forum, social media e altri campi in cui è previsto un input dell'utente arbitrario un po 'arbitrario ed è una parte normale dell'applicazione. L'autore dell'attacco può includere lo script in un post del forum in una parte altrimenti valida di una conversazione. Ogni volta che qualcuno visualizza il post, lo script verrà eseguito.

Un attacco XSS riflesso

Nel secondo tipo di attacco cross-site scripting, noto come script cross-site riflesso, l'utente malintenzionato consegna lo script iniettato al sito vulnerabile in modo che venga immediatamente restituito all'utente. Metodi comuni per farlo, pagine di destinazione in cui l'input dell'utente diventa parte dell'output di una pagina. Una pagina di ricerca può visualizzare i termini di ricerca per l'utente e potrebbe fornire uno sbocco per questo attacco. Lo script inserito nell'input di un utente non dovrebbe mai essere memorizzato dall'applicazione web.

Attacchi basati su DOM

Il terzo attacco di cross site scripting avviene interamente nel browser. L'attacco funziona manipolando il modello interno della pagina web all'interno del browser noto come DOM e si riferisce a come attacchi basati su DOM. Questi ancora, consentono all'utente malintenzionato di eseguire codice dannoso, ma il codice restituito dal server viene manipolato in JavaScript eseguibile dalla pagina Web.

In definitiva, un attacco cross-site scripting è un attacco cross-site scripting, indipendentemente da come viene fornito. Poiché il codice inserito proviene da un server altrimenti attendibile, può spesso essere eseguito sotto le autorizzazioni del sito. Pertanto può agire come se fosse codice nativo sul sito web. 

Un attacco di scripting cross site di successo potrebbe consentire l'accesso ai cookie su una pagina web. Questi cookie possono contenere informazioni sensibili, inclusi identificativi di sessione che consentirebbero a chi effettua l'attacco di impersonare l'utente attaccato. L'attacco può anche modificare il contenuto HTML su una pagina per visualizzare un modulo di accesso falso e rubare le credenziali di accesso dell'utente. L'utente malintenzionato potrebbe esaminare e inviare qualsiasi contenuto della pagina consentendo l'acquisizione di informazioni sensibili come i numeri di account. Un attacco più avanzato potrebbe, in effetti, installare un keylogger che invia qualsiasi informazione inserita nella pagina Web a un utente malintenzionato.

Protezione da attacchi cross-site scripting

Attenuare gli script cross-site non richiede la fiducia di alcun input da un utente o da qualsiasi altra fonte esterna. L'applicazione web deve considerare questi dati potenzialmente pericolosi, indipendentemente dalla fonte. Diamo un'occhiata ad alcuni metodi specifici di ASP.NET per prevenire questi attacchi usando componenti integrati nel framework e librerie disponibili gratuitamente.

Convalida tutto l'input

L'applicazione Web deve convalidare qualsiasi input per l'applicazione prima che venga utilizzata. Proprio come con altri attacchi di iniezione come SQL Injection. L'applicazione preferibilmente convalida questo input con una lista bianca di valori accettabili. La convalida rimuove o sostituisce eventuali componenti imprevisti dell'input con un valore codificato. È possibile utilizzare un metodo di blacklist, che rimuove solo un elenco di caratteri indesiderati noti, ma è più vulnerabile ai nuovi metodi di attacco.

Se sappiamo che un valore dovrebbe sempre essere un numero intero, puoi convalidare l'input usando un codice come:

int memberId; if (! int.TryParse (externalValue, out memberId)) return RedirectToAction ("InputError");  

Se il framework non può analizzare il recuperato in precedenza externalValue come numero intero, il codice reindirizza a una pagina che visualizza un errore. Altrimenti lo sappiamo memberId contiene un valore intero. Questo processo funziona anche con altri tipi di base. Alcuni tipi più comuni forniscono anche metodi per convalidare le informazioni. La rete Uri la classe contiene un metodo IsWellFormedUriString che può convalidare un URL. Ciò consentirebbe la convalida che la voce della home page di un utente contenga un URL valido prima della visualizzazione.

var userHomePage = userRecord ["homepage"]; if (! Uri.IsWellFormedUriString (newUrl, UriKind.Absolute)) Model.homepage = "none";  else Model.homepage = Html.Encode (userHomePage);  

Altri tipi di dati più complessi richiedono una convalida più complessa. La convalida del campo del numero di una carta di credito può rimuovere qualsiasi carattere nella stringa che non sia una cifra. La convalida di stringhe più complesse potrebbe richiedere espressioni regolari. La convalida di una classe potrebbe richiedere anche controlli più complessi.

Convalida della richiesta ASP.NET

ASP.NET fornisce una protezione efficace contro gli attacchi riflessi usando la convalida delle richieste. Se ASP.NET rileva markup o codice in una richiesta, genera un'eccezione "potenzialmente pericoloso rilevato" e interrompe l'elaborazione della richiesta.

Anche se prezioso, ci sono momenti in cui è necessario consentire questi valori in una richiesta. Un esempio comune arriva consentendo il rich text in un modulo. In questi casi, purtroppo, la richiesta di convalida è troppo spesso disattivata per l'intero sito. Una soluzione migliore disattiva questa convalida solo dove necessario. Nelle versioni precedenti di ASP.NET, aggiungendo validateRequest = "false" al Pagina direttiva in Webforms disattiva la validazione per una pagina. In ASP.NET MVC, aggiungendo il [ValidateInput (false)] attributo a un'azione del controllore disattiva la convalida per quell'azione, mentre aggiunge il [AllowHtml] l'attributo disattiva la convalida per un campo.

ASP.NET 4.0 ha modificato la convalida della richiesta in diversi modi. Questa e le versioni successive del framework fanno la convalida all'inizio della richiesta HTTP. La convalida si applica anche a tutte le richieste ASP.NET e non solo .aspx richieste di pagina. Questo include anche moduli HTTP personalizzati. Le pagine che si basano sul comportamento originale possono ripristinare il metodo precedente impostando il requestValidationMode attributo nel web.config file alla versione 2.0.

 

Ancora meglio, è quello di disabilitare questo solo per le pagine dove necessario, utilizzando la sintassi in web.configfile:

     

ASP.NET 4.5 ha aggiunto la possibilità di rinviare la convalida fino alla richiesta dei dati. Impostazione del requestValidationMode attributo nel tuo web.config file alla versione 4.5 attiva questo nuovo comportamento.

 

Anche ASP.NET 4.5 ha aggiunto il HttpRequest.Unvalidated proprietà. L'utilizzo di questa proprietà consente un accesso più semplice al valore del modulo non convalidato, laddove necessario. Combinando la convalida ritardata e il unvalidated proprietà, è possibile accedere ai valori non convalidati quando necessario, ma proteggere gli altri input di modulo.

Codifica HTML

Prima di visualizzare dati esterni su una pagina web, il codice HTML deve essere codificato in modo che non venga elaborato dal browser. Ad esempio, prendere una pagina ASP.NET scritta in modo che possa essere passato un messaggio per la visualizzazione, ad esempio un aggiornamento di stato. Un'applicazione può utilizzare questa pagina per mostrare all'utente che il suo account è stato creato senza errori. Normalmente l'URL di questa pagina appare simile http: // appname / PlaceOrder / Account + Creato. La pagina risultante mostra il messaggio all'utente con un campo, come ad esempio:

<%= Html.Label("Message", Model.message) %> 

... e viene visualizzato come:

Se cambiamo la chiamata dell'URL su http: / appname / PlaceOrder /, ora otteniamo qualcosa di diverso.

La sceneggiatura potrebbe essere qualsiasi cosa, naturalmente, e non solo l'innocua casella di avviso che appare qui. La convalida della richiesta catturerebbe gli esempi precedenti e restituirà un'eccezione prima della visualizzazione. Se disattivato, la codifica dell'output previene l'attacco.

ASP.NET semplifica la codifica dei dati al fine di prevenire gli attacchi. Le prime versioni di MVC che utilizzavano la sintassi di Webform contenevano spesso codice come questo che non codificava HTML.

<%= status >

Dovevi codificare manualmente l'output in modo che qualsiasi HTML venisse convertito in un formato di visualizzazione. Così la < il personaggio diventa la stringa <. Il Html.Encode la funzione fornisce questa conversione. La forma più sicura di codice diventa così:

<%= Html.Encode(status) >

ASP.NET MVC in seguito ha introdotto una sintassi per fare ciò in un unico passaggio sostituendo <= con <: quindi il codice può essere abbreviato in:

<%: status >

Usando il motore di visualizzazione Razor, tutti gli output sono codificati in HTML a meno che non si usi specificamente un metodo per non codificarlo. In Razor, il codice equivalente a quanto sopra diventa:

@stato

Razor gestisce automaticamente la codifica HTML di qualsiasi stringa stato contiene. Nel caso in cui è necessario rendere i dati grezzi, è possibile utilizzare il HTML.Raw () metodo. Per visualizzare il risultato senza codifica, possiamo usare:

@ Html.Raw (stato)

In questo esempio, il codice sopra renderebbe la nostra applicazione vulnerabile ancora. Quindi, ci sono alcune circostanze in cui non dovresti codificare l'output. Se si disattiva questa funzione su un campo, è necessario fare particolare attenzione per garantire che i dati siano disinfettati prima della visualizzazione. Fortunatamente, c'è una libreria che aiuta con questo, mentre fa anche di più per proteggere la tua applicazione dallo scripting cross-site.

Libreria AntiXSS

Se stai scrivendo un'applicazione ASP.NET, dovresti utilizzare AntiXSS Library per ASP.NET. Dal sito Web del progetto, "AntiXSS offre una miriade di funzioni di codifica per l'input dell'utente, tra cui HTML, attributi HTML, XML, CSS e JavaScript".

La libreria contiene metodi mirati alla disinfezione di dati esterni in base all'uso previsto di tali dati. Questi metodi utilizzano l'approccio basato sulla lista bianca preferita. Ciò significa che i dati codificati, pensati per un attributo HTML, possono essere disinfettati per contenere solo dati validi per un attributo HTML. Il tradizionale ASP.NET HtmlEncode i metodi usano l'approccio black list che codifica solo alcuni caratteri potenzialmente pericolosi.

Microsoft ha iniziato a includere routine core da questa libreria in ASP.NET 4.5 in una nuova System.Web.Security.AntiXss namespace. È inoltre possibile configurare il framework per utilizzare questi metodi AntiXSS al posto delle routine di codifica incorporate. Lo fai impostando il encoderType attributo di httpRuntime nel web.config file per l'applicazione:

 

Se l'applicazione esegue una visualizzazione significativa di dati esterni, l'uso di AntiXSS farà molto per proteggere la tua applicazione dallo scripting cross-site. Se si utilizza ASP.NET 4.5, la modifica dell'applicazione per utilizzare i nuovi metodi AntiXSS per la codifica predefinita offre una protezione ancora maggiore per l'applicazione Web.

In sintesi

Prevenire lo scripting cross-site è più difficile di quanto non sembri inizialmente. OWASP elenca oltre 80 vettori che possono essere scelti come target tramite attacchi di cross site scripting. Tale organizzazione elenca anche queste vulnerabilità come terza nell'elenco 2013 delle principali vulnerabilità del Web.

Se non si garantisce che tutti i dati esterni introdotti nell'applicazione siano debitamente fuoriusciti o non convalidi l'input prima di inserirlo in una pagina di output, si lascia la tua applicazione Web vulnerabile allo scripting cross-site. In ASP.NET, questo può essere fatto da:

  1. Convalida di tutti gli input esterni all'applicazione prima di visualizzarli su una pagina Web.
  2. Utilizzare Convalida richiesta ovunque che l'applicazione non abbia specificamente bisogno di disattivarlo, ad esempio un modulo che consente un input HTML avanzato. Se è necessario consentire le informazioni non convalidate, lasciare la convalida in qualsiasi altro punto della propria applicazione.
  3. Codifica HTML prima di visualizzare dati esterni su una pagina web
  4. Utilizzare i metodi basati su AntiXSS inclusi in ASP.NET 4.5 e utilizzare la libreria AntiXSS per le versioni precedenti di ASP.NET.