Le pagine e i controlli Web sono la prima linea difensiva dell'applicazione e possono diventare oggetto di una profonda analisi da parte di pirati informatici intenzionati a compromettere la protezione dell'applicazione. Spesso l'obiettivo finale delle aggressioni è di invadere i sistemi back-end e gli archivi di dati.
Un attacco riuscito, come l'inserimento di codice (code injection) o l'elaborazione di script tra siti (XSS, Script tra siti), sfrutta la vulnerabilità dell'applicazione sul lato server e i suoi risultati possono essere devastanti portando alla divulgazione di informazioni, allo spoofing (falsificazione) dell'identità, all'acquisizione di privilegi elevati e all'esecuzione remota di codice. Per creare pagine e controlli Web protetti, è necessario attenersi alle procedure di programmazione corrette descritte in questo modulo.
Verranno innanzitutto elencati e spiegati i pericoli più frequenti che incombono su pagine e controlli ASP.NET nonché le contromisure associate. Seguirà quindi un elenco completo delle aree di protezione dell'applicazione interessate, tra cui la convalida dell'input, la codifica dell'output, l'autenticazione, l'autorizzazione, la rappresentazione, la protezione di dati riservati, la gestione protetta delle sessioni, la protezione della manipolazione dei parametri e la gestione delle eccezioni. Queste tecniche costituiscono una parte fondamentale della soluzione di protezione denominata "difesa a più livelli". Poiché sono spesso trascurati, i pericoli descritti consentono ai pirati informatici di riuscire a compromettere il sistema indipendentemente dal livello di protezione dell'infrastruttura.
Il modulo consente di:
| • | Progettare pagine e controlli ASP.NET protetti. |
| • | Sviluppare codice di convalida protetto utilizzando espressioni regolari e altre tecniche. |
| • | Impedire l'elaborazione di script tra siti (XSS). |
| • | Autenticare e autorizzare gli utenti. |
| • | Sviluppare un'autenticazione dei form protetta. |
| • | Impedire che i dettagli delle eccezioni, pieni di informazioni, raggiungano il client. |
| • | Gestire e proteggere le sessioni ASP.NET. |
| • | Prevenire la manipolazione dei parametri. |
| • | Individuare le contromisure da applicare per risolvere pericoli frequenti come l'inserimento di codice, il dirottamento di sessione, lo spoofing di identità, la manipolazione dei parametri, l'ascolto e l'intercettazione di rete non autorizzati, la divulgazione di informazioni, gli script tra siti (XSS) e gli attacchi con riproduzione di cookie. |
Le informazioni contenute in questo modulo sono valide per i seguenti prodotti e tecnologie:
| • | Microsoft® Windows® 2000 Server e Microsoft Windows Server™ 2003 |
| • | Microsoft .NET Framework 1.1 e ASP.NET 1.1 |
Oltre alle procedure di programmazione protetta illustrate, utilizzare i corrispondenti form di questa guida al fine di creare pagine e controlli ASP.NET protetti.
| • | Implementare la procedura descritta nel modulo 19 "Protezione dell'applicazione ASP.NET e dei Servizi Web", che consente di configurare ASP.NET appropriatamente con impostazioni di protezione in Machine.config e Web.config. |
| • | Utilizzare l'elenco di controllo associato riportato nell'apposita sezione di questa guida. In "Elenco di controllo: Protezione di ASP.NET" vengono riunite le raccomandazioni illustrate nel presente modulo e nel modulo 19. Avere cura di rispettarle. |
| • | Acquisire una conoscenza dei pericoli e degli attacchi che sono specifici di pagine e controlli ASP.NET. Applicare le contromisure conformemente alle linee guida contenute in questo modulo. |
| • | Leggere il modulo 4 "Linee guida di progettazione per applicazioni Web protette". Molte delle raccomandazioni contenute nel presente modulo 10 si basano sulle linee guida di progettazione descritte nel modulo 4. |
| • | I progettisti sono invitati a utilizzare la sezione "Considerazioni sulla progettazione" di questo modulo. |
| • | Gli sviluppatori dovrebbero applicare i consigli contenuti nel modulo al processo di sviluppo di cui si occupano e rivolgere un'attenzione speciale alla convalida dei dati di input in quanto la maggior parte dei principali attacchi a livello di applicazione prende di mira le vulnerabilità di questa area. |
| • | Considerare i controlli dal punto di vista della programmazione per ottimizzare la protezione di pagine e controlli ASP.NET. |
| • | Servirsi delle categorie di vulnerabilità delle applicazioni come strumento per affrontare problemi comuni. Esse costituiscono un utile metodo di raggruppamento e risoluzione dei problemi. |
Nella maggior parte degli attacchi via Web alle applicazioni l'input dannoso passa attraverso richieste HTTP. L'obiettivo generale è di indurre l'applicazione a eseguire operazioni non autorizzate o di comprometterne il normale funzionamento. Per questa ragione la convalida dell'input è una contromisura essenziale per molti attacchi e dovrebbe essere una priorità durante lo sviluppo di pagine e controlli ASP.NET. I pericoli principali sono i seguenti:
| • | Code injection (inserimento di codice) |
| • | Dirottamento di sessione |
| • | Spoofing (falsificazione) dell'identità |
| • | Manipolazione dei parametri |
| • | Ascolto e intercettazione di rete non autorizzati |
| • | Divulgazione di informazioni |
Nella figura 10.1 sono riepilogate le minacce più comuni che incombono sulle applicazioni Web.

Figura 10.1
Minacce comuni per pagine e controlli Web ASP.NET
L'inserimento di codice si verifica quando un pirata informatico riesce a eseguire del codice arbitrario utilizzando il contesto di protezione dell'applicazione. Il rischio aumenta se l'applicazione viene eseguita da un account con privilegi.
Attacchi
Esistono vari tipi di attacco con inserimento di codice tra cui:
| • | Script tra siti. Lo script dannoso viene inviato a un'applicazione Web come input. Viene quindi rinviato al browser di un utente, dove viene eseguito. |
| • | Overflow del buffer. La verifica indipendente dai tipi del codice gestito riduce notevolmente il rischio ma l'applicazione rimane vulnerabile, soprattutto laddove chiama codice non gestito. Gli overflow del buffer possono consentire a un pirata informatico di eseguire del codice arbitrario nel processo dell'applicazione Web, utilizzandone il contesto di protezione. |
| • | Inserimento di SQL. Questo tipo di attacco prende di mira la vulnerabilità del codice di accesso ai dati. Il pirata informatico invia un input SQL che altera la query desiderata o esegue query del tutto nuove nel database. Solitamente gli obiettivi sono pagine di accesso per l'autenticazione basata su form perché il nome utente e la password sono utilizzati per inviare query all'archivio dell'utente. |
Vulnerabilità
Tra le vulnerabilità che possono provocare la riuscita degli attacchi con inserimento di codice sono incluse le seguenti:
| • | convalida dell'input debole o inesistente oppure convalida dell'input sul lato client; |
| • | inclusione di input non convalidato in output HTML; |
| • | sviluppo dinamico di istruzioni SQL in cui non sono utilizzati parametri tipizzati; |
| • | utilizzo di account di processi con più privilegi del necessario e di procedure di accesso al database. |
Contromisure
Per evitare l'inserimento di codice, è possibile utilizzare le seguenti contromisure:
| • | Convalidare l'input, impedendo il tal modo al pirata informatico di inserire del codice di script o di provocare l'overflow del buffer. |
| • | Codificare tutto l'output in cui è incluso l'input. In tal modo si evita che i tag di script potenzialmente dannosi vengano interpretati come codice nel browser del client. |
| • | Utilizzare stored procedure che accettano i parametri per evitare che l'input SQL dannoso possa essere interpretato dal database come una serie di istruzioni eseguibili. |
| • | Utilizzare account di processi e di rappresentazione con privilegi minimi. Ciò riduce il rischio e attenua il danno che potrebbe essere provocato se il pirata informatico riuscisse a eseguire il codice sfruttando il contesto di protezione dell'applicazione. |
Il dirottamento di sessione si verifica quando il pirata informatico entra in possesso di un token di autenticazione e assume il controllo della sessione di un altro utente. I token di autenticazione sono spesso memorizzati in cookie o in URL. Se il pirata informatico si impossessa del token di autenticazione, è in grado di trasmetterlo all'applicazione unitamente a una richiesta. L'applicazione associa quindi la richiesta alla sessione dell'utente legittimo e ciò consente al pirata informatico di accedere alle aree con restrizioni dell'applicazione che richiedono l'accesso autenticato. Il pirata assume quindi l'identità e i privilegi dell'utente legittimo.
Vulnerabilità
Tra i frequenti punti vulnerabili che espongono pagine e controlli Web al dirottamento di sessione figurano i seguenti:
| • | ID di sessione non protetti negli URL; |
| • | cookie di rappresentazione memorizzati insieme a cookie di autenticazione; |
| • | cookie di autenticazione trasmessi attraverso collegamenti non crittografati. |
Attacchi
Gli attacchi con dirottamento di sessione comprendono:
| • | Riproduzione di cookie. Il pirata informatico si impossessa del cookie di autenticazione utilizzando un software per il monitoraggio della rete o con altri mezzi, ad esempio sfruttando la vulnerabilità degli script tra siti. |
| • | Manipolazione delle stringhe di query. L'utente malintenzionato modifica l'ID di sessione, chiaramente indicato nella stringa di query dell'URL. |
Contromisure
Per evitare il dirottamento di sessione, è possibile applicare le contromisure seguenti:
| • | Tenere separati i cookie di rappresentazione da quelli di autenticazione. |
| • | Trasmettere i cookie di autenticazione solo attraverso connessioni HTTPS. |
| • | Non consentire la visualizzazione nelle stringhe di query di ID di sessione che rappresentano utenti autenticati. |
| • | Eseguire una nuova autenticazione dell'utente prima di operazioni importanti, come l'inoltro di ordini, i trasferimenti di denaro e così via. |
Lo spoofing dell'identità si verifica quando un utente malintenzionato assume l'identità di un utente legittimo allo scopo di accedere all'applicazione.
Vulnerabilità
Tra i frequenti punti vulnerabili che espongono pagine e controlli Web ad attacchi con spoofing dell'identità figurano i seguenti:
| • | credenziali di autenticazione trasmesse attraverso collegamenti non crittografati; |
| • | cookie di autenticazione trasmessi attraverso collegamenti non crittografati; |
| • | password e criteri poco efficaci; |
| • | archiviazione delle credenziali poco efficace nell'archivio dell'utente. |
Attacchi
Gli attacchi con spoofing dell'identità comprendono:
| • | Riproduzione di cookie. Il pirata informatico si impossessa del cookie di autenticazione utilizzando un software per il monitoraggio della rete o sfruttando la vulnerabilità degli script tra siti. Invia quindi il cookie all'applicazione per accedervi sotto mentite spoglie. |
| • | Attacchi alla password di tipo "brute force". Il pirata informatico prova ripetutamente varie combinazioni di nome utente e password. |
| • | Attacchi al dizionario. È una forma automatizzata di violazione della password di tipo "brute force" in cui viene provata come password ogni parola contenuta in un dizionario. |
Contromisure
Per evitare lo spoofing dell'identità, è possibile applicare le contromisure seguenti:
| • | Trasmettere credenziali e cookie di autenticazione solo attraverso connessioni HTTPS. |
| • | Impiegare password complesse. Per avere la certezza che le password fornite dagli utenti rispondano al livello di complessità richiesto, è possibile utilizzare espressioni regolari. |
| • | Archiviare nel database gli strumenti di verifica delle password. Per ridurre il rischio di attacchi al dizionario, archiviare hash di password non reversibili unitamente a un valore salt casuale. |
Per ulteriori informazioni sull'archiviazione di hash di password nel database e di altri dati segreti, vedere il modulo 14 "Creazione di codice di accesso ai dati protetto".
Per parametri si intendono i singoli dati trasmessi in rete dal client al server. Comprendono i campi dei form, le stringhe di query, lo stato di visualizzazione, i cookie e le intestazioni HTTP. Se i dati riservati o i dati utilizzati per prendere decisioni di protezione nel server vengono trasmessi con parametri non protetti, l'applicazione è potenzialmente esposta alla divulgazione di informazioni o all'accesso non autorizzato.
Vulnerabilità
Fra le vulnerabilità che possono portare alla manipolazione di parametri vi sono:
| • | utilizzo di campi di form nascosti o di stringhe di query che contengono dati riservati; |
| • | trasmissione di cookie contenenti dati sensibili a livello di protezione attraverso connessioni non crittografate. |
Attacchi
Gli attacchi con manipolazione dei parametri comprendono:
| • | Attacchi con riproduzione di cookie. Il pirata informatico entra in possesso di un cookie e lo altera per riprodurlo in seguito nell'applicazione. Se il cookie contiene dati utilizzati per l'autenticazione o l'autorizzazione nel server, questo metodo può portare allo spoofing dell'identità o all'acquisizione di privilegi più elevati. |
| • | Manipolazione di campi di form nascosti. Questi campi contengono dati utilizzati per prendere decisioni a livello di protezione nel server. |
| • | Manipolazione di parametri in stringhe di query. |
Contromisure
Per evitare la manipolazione dei parametri, è possibile applicare le contromisure seguenti:
| • | Non fare affidamento sulle opzioni di gestione dello stato sul lato client. Evitare di utilizzare le opzioni di gestione dello stato sul lato client, come lo stato di visualizzazione, i cookie, le stringhe di query o campi di form nascosti, per archiviare dati riservati. |
| • | Archiviare i dati riservati nel server. Utilizzare un token di sessione per associare la sessione dell'utente ai vari dati riservati conservati nel server. |
| • | Utilizzare un codice di autenticazione dei messaggi (MAC, Message Authentication Code) per proteggere il token di sessione. Abbinare quest'ultimo nel server alla logica aziendale, di autenticazione e autorizzazione per avere la certezza che non venga riprodotto. |
L'ascolto e l'intercettazione di rete non autorizzati implicano l'utilizzo di software per il monitoraggio della rete per rintracciare i pacchetti di dati trasmessi tra il browser e il server Web. Ciò può portare alla divulgazione di dati riservati specifici dell'applicazione, al recupero di credenziali di accesso o all'acquisizione di cookie di autenticazione.
Vulnerabilità
Tra le vulnerabilità che possono portare all'ascolto e all'intercettazione di rete non autorizzati figurano:
| • | Assenza di crittografia per l'invio di dati riservati |
| • | Invio di cookie di autenticazione attraverso canali non crittografati |
Attacchi
Gli attacchi che si basano sull'ascolto e l'intercettazione di rete non autorizzati vengono eseguiti mediante strumenti di sniffing dei pacchetti posizionati in rete per intercettare il traffico.
Contromisure
Per contrastare l'ascolto e l'intercettazione di rete non autorizzati, utilizzare il protocollo SSL (Secure Sockets Layer) che consente di fornire un canale di comunicazione crittografato tra il browser e il server Web. Ogni volta che in rete vengono trasmessi credenziali, ticket di autenticazione o dati di applicazione riservati, è estremamente importante utilizzare il protocollo SSL.
La diffusione di informazioni si verifica quando un pirata informatico analizza le pagine Web alla ricerca del modo per provocare delle eccezioni. Si tratta di un esercizio molto utile per il pirata informatico in quanto i dettagli contenuti nelle eccezioni, spesso forniti in HTML e visualizzati nel browser, possono diffondere informazioni estremamente utili, come tracce dello stack contenenti stringhe di connessione al database, nomi dei database, informazioni sullo schema dei database, istruzioni SQL e versioni del sistema operativo e della piattaforma utilizzata.
Vulnerabilità
Tra le vulnerabilità che portano alla diffusione delle informazioni figurano:
| • | Gestione delle eccezioni poco efficace |
| • | Possibilità che i dettagli non elaborati contenuti nelle eccezioni raggiungano il client |
Attacchi
Molti attacchi portano alla diffusione delle informazioni, tra cui:
| • | Overflow del buffer. |
| • | Invio deliberato di input non valido. |
Contromisure
Per evitare la diffusione delle informazioni:
| • | Utilizzare una gestione delle eccezioni strutturata. |
| • | Restituire al client pagine di errore generiche. |
| • | Utilizzare pagine di reindirizzamento predefinite contenenti messaggi di errore generici e inoffensivi. |
Prima di sviluppare pagine e controlli Web, è opportuno prendere in considerazione alcuni punti importanti in fase di progettazione. Le considerazioni seguenti sono di primaria importanza:
| • | Utilizzare la convalida dell'input sul lato server. |
| • | Partizionare il sito Web. |
| • | Prendere in considerazione l'identità utilizzata per l'accesso alle risorse. |
| • | Proteggere credenziali e ticket di autenticazione. |
| • | Ridurre i rischi in caso di errore. |
| • | Prendere in considerazione la granularità dell'autorizzazione. |
| • | Inserire controlli Web e controlli utente in assembly distinti. |
| • | Inserire il codice di accesso alle risorse in un assembly separato. |
Durante la progettazione, identificare tutte le varie origini di input utente che verranno elaborate dalle pagine e dai controlli Web, compresi i campi di form, le stringhe di query e i cookie ricevuti dall'utente Web, nonché dati provenienti da origini dati back-end. Ovviamente l'utente Web si trova al di fuori dei confini delle zone protette dell'applicazione pertanto tutto l'input proveniente da questa fonte deve essere convalidata nel server. Prima che i dati possano essere inviati al client, devono essere convalidati e sterilizzati a meno che non si tratti di dati provenienti da origini back-end assolutamente attendibili. Avere cura di sviluppare una soluzione in cui la convalida non avviene sul lato client perché in tal modo può essere facilmente ignorata.
Nella progettazione del sito Web è necessario stabilire una netta distinzione tra aree accessibili pubblicamente e aree con restrizioni che richiedono l'accesso autenticato. Per gestire pagine con restrizioni, come le funzioni di uscita dal sito nei siti Web di e-commerce classici, in cui l'accesso deve essere autenticato e in cui vengono trasmessi dati riservati come i numeri delle carte di credito, utilizzare sottodirectory distinte nella directory principale virtuale dell'applicazione. Le sottodirectory distinte consentono di applicare ulteriore protezione, ad esempio richiedendo il protocollo SSL, senza provocare un sovraccarico delle prestazioni SSL in tutto il sito. Consentono inoltre di ridurre il rischio di dirottamento di sessione limitando la trasmissione di cookie di autenticazione alle connessioni HTTPS. Nella figura 10.2 è illustrato un partizionamento classico.

Figura 10.2
Sito Web suddiviso in un'area pubblica e un'area protetta
Notare, nella figura 10.2, come per la sottocartella con restrizioni sia configurato in Internet Information Services (IIS) l'accesso SSL obbligatorio. Il primo elemento <authorization> in Web.config consente a tutti gli utenti di accedere all'area pubblica mentre il secondo impedisce agli utenti non autenticati di accedere al contenuto della sottocartella protetta e impone l'esecuzione di una procedura di accesso.
Per ulteriori informazioni sull'applicazione di restrizioni ai cookie di autenticazione affinché vengano trasmessi solo attraverso connessioni HTTPS nonché sulla modalità di spostamento tra pagine con restrizioni e pagine senza restrizioni, vedere "Utilizzare URL assoluti per gli spostamenti" nella sezione "Autenticazione" di questo modulo.
Poiché per impostazione predefinita nelle applicazioni ASP.NET non avviene alcuna rappresentazione, per eseguire le applicazioni Web ASP.NET e per accedere alle risorse viene utilizzato l'account di processi ASPNET con privilegi minimi. L'impostazione predefinita è la configurazione consigliata. Esistono molte situazioni in cui potrebbe essere necessario utilizzare un contesto di protezione di Windows diverso per l'accesso alle risorse, tra cui:
| • | Più applicazioni in esecuzione nello stesso server |
| • | Accesso a una risorsa remota con requisiti di autenticazione specifici |
È opportuno includere nella progettazione la modalità di protezione di credenziali e ticket di autenticazione. Le credenziali devono essere protette se vengono trasmesse in rete nonché quando si trovano in archivi permanenti come i file di configurazione. I ticket di autenticazione devono essere protetti in rete perché sono esposti al dirottamento informatico. La crittografia rappresenta una buona soluzione. Per la protezione delle credenziali e dei ticket in rete sono indicati i protocolli SSL o IPSec mentre per la crittografia delle credenziali nei file di configurazione una buona soluzione è costituita da DPAPI.
Se nell'applicazione si verifica un errore con una condizione di eccezione irreversibile, accertarsi che vengano ridotti i possibili rischi correlati evitando di lasciare il sistema del tutto aperto. Fare in modo che i dettagli delle eccezioni, contenenti informazioni utili per l'utente malintenzionato, non giungano al client e che vengano invece visualizzate pagine di errore generiche. Prendere in considerazione la gestione strutturata delle eccezioni anziché fare affidamento sui codici di errore.
Prendere in considerazione la granularità dell'autorizzazione utilizzata nelle parti autenticate del sito. Se per una directory è stata configurata l'autenticazione obbligatoria, tutti gli utenti potranno accedere senza distinzioni alle pagine contenute nella directory? Se necessario, è possibile applicare alle varie pagine diverse regole di autorizzazione in base all'identità, o più comunemente all'appartenenza a ruoli, del chiamante, utilizzando vari elementi <authorization> all'interno di elementi <location> distinti.
Due pagine contenute nella stessa directory, ad esempio, possono avere elementi <allow> e <deny> diversi in Web.config.
Quando i controlli Web e i controlli utente sono nei rispettivi assembly, è possibile configurare la protezione di ciascun assembly indipendentemente utilizzando criteri di protezione dall'accesso di codice. L'amministratore potrà così godere di ulteriore flessibilità e lo sviluppatore non sarà obbligato a concedere autorizzazioni estese a tutti i controlli solo per soddisfare i requisiti di un unico controllo.
Utilizzare assembly separati e chiamarli dalle classi delle pagine anziché incorporare il codice di accesso alle risorse nel gestore eventi delle classi delle pagine. Ciò aumenta la flessibilità dei criteri di protezione dall'accesso di codice ed è particolarmente importante per la creazione di applicazioni Web con attendibilità parziale. Per ulteriori informazioni, leggere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET".
Se si formulano ipotesi prive di fondamento sul tipo, la lunghezza, il formato o l'intervallo dell'input, è improbabile che l'applicazione si riveli solida. La convalida dell'input potrebbe diventare un problema a livello di protezione se un pirata informatico scoprisse che sono state formulate delle ipotesi infondate. Chi effettua l'attacco può quindi creare degli input che possono compromettere l'applicazione. La fiducia mal riposta nell'input dell'utente è una delle vulnerabilità più frequenti e devastanti delle applicazioni Web.
Innanzitutto porre dei vincoli e verificare i dati validi noti convalidando il tipo, la lunghezza, il formato e l'intervallo. È a volte necessario sterilizzare l'input e rendere sicuro quello potenzialmente dannoso. Se, ad esempio, l'applicazione supporta i campi di input a formato libero, come quelli in cui inserire commenti, è consigliabile consentire alcuni elementi HTML "sicuri", come <b> e <i>, eliminando tutti gli altri elementi HTML. Nella tabella seguente sono riepilogate le opzioni disponibili per vincolare e sterilizzare i dati:
Tabella 10.1 Opzioni disponibili per vincolare e sterilizzare i dati
| Requisito | Opzioni |
Verifiche del tipo | Sistema dei tipi .NET Framework. Analizzare i dati delle stringhe, convertire in un tipo più sicuro quindi gestire FormatExceptions. |
Verifiche della lunghezza | Espressioni regolari |
Verifiche del formato | Espressioni regolari per corrispondenza di schemi |
Verifiche dell'intervallo | Controllo ASP.NET RangeValidator (supporta valuta, data, valori INTEGER, Double e dati di stringa) |
L'utilizzo delle espressioni regolari consente di limitare il numero dei caratteri validi, eliminare quelli non desiderati ed eseguire verifiche a livello di lunghezza e formato. È possibile porre dei vincoli al formato dell'input definendo schemi ai quali l'input deve corrispondere. In ASP.NET è fornito il controllo RegularExpressionValidator e la classe Regex è disponibile dallo spazio dei nomi System.Text.RegularExpressions.
Se si utilizzano i controlli di convalida, questa va a buon fine se il controllo è vuoto. Per i campi obbligatori utilizzare RequiredFieldValidator. L'implementazione della convalida delle espressioni regolari, inoltre, è leggermente diversa nel client rispetto al server. Nel client viene utilizzata la sintassi delle espressioni regolari di Microsoft Jscript (software di sviluppo). Nel server viene utilizzata la sintassi System.Text.RegularExpressions.Regex. Poiché la sintassi JScript delle espressioni regolari è un sottoinsieme della sintassi System.Text.RegularExpressions.Regex, si consiglia di utilizzarla per produrre gli stessi risultati tanto nel client quanto nel server.
Per ulteriori informazioni su tutti i controlli di convalida ASP.NET, fare riferimento alla documentazione di .NET Framework.
Per convalidare l'input nei campi di Web form, è possibile utilizzare il controllo RegularExpressionValidator. Trascinare il controllo su un Web form e impostare le relative proprietà ValidationExpression, ControlToValidate e ErrorMessage.
Per impostare l'espressione di convalida, utilizzare la finestra delle proprietà di Microsoft Visual Studio® .NET oppure impostare la proprietà dinamicamente nel gestore eventi Page_Load. Quest'ultimo approccio consente di raggruppare tutte le espressioni regolari di tutti i controlli della pagina.
Se si utilizzano controlli HTML regolari senza proprietà runat="server" (cosa che esclude il controllo RegularExpressionValidator) oppure se è necessario convalidare l'input proveniente da altre origini, come stringhe di query o cookie, è possibile utilizzare la classe Regex nella classe della pagina o in un metodo di supporto alla convalida, possibilmente in un assembly separato. Nel prosieguo della sezione sono illustrati alcuni esempi.
Le espressioni regolari risultano più chiare se si utilizza la sintassi seguente e se si commentano i singoli componenti con il simbolo #. Per poter inserire i commenti è inoltre necessario specificare RegexOptions.IgnorePatternWhitespace, vale a dire che lo spazio vuoto non preceduto da caratteri escape viene ignorato.
Regex regex = new Regex(@"
^ # anchor at the start
(?=.*\d) # must contain at least one digit
(?=.*[a-z]) # must contain one lowercase
(?=.*[A-Z]) # must contain one uppercase
.{8,10} # From 8 to 10 characters in length
$ # anchor at the end",
RegexOptions.IgnorePatternWhitespace);Per convalidare i campi di stringa, come nomi, indirizzi, codici fiscali e così via, servirsi delle espressioni regolari per conseguire i risultati seguenti:
| • | Porre dei limiti ai caratteri di input accettabili. |
| • | Applicare regole di formattazione. Alcuni campi a schema fisso, come ad esempio i codici fiscali o i codici postali, impongono schemi specifici ai caratteri di input. |
| • | Verificare le lunghezze. |
Nell'esempio seguente è mostrato un controllo RegularExpressionValidator utilizzato per convalidare un campo per l'immissione del nome.
<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtName" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="nameRegex"runat="server"
ControlToValidate="txtName"
ValidationExpression="^[a-zA-Z'.`-´\s]{1,40}$"
ErrorMessage="Invalid name">
</asp:regularexpressionvalidator>
</form>
In questa espressione di convalida si applica al campo di immissione del nome il vincolo dei caratteri alfabetici (minuscole e maiuscole), dell'apostrofo singolo per i nomi come O'Dell e del punto. Si limita inoltre la lunghezza del campo a 40 caratteri.
Nell'esempio seguente è mostrato il codice HTML generato per un controllo RegularExpressionValidator utilizzato per convalidare il campo di un form in cui immettere un numero di previdenza sociale americano:
<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtSSN" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="ssnRegex" runat="server"
ErrorMessage="Invalid social security number"
ValidationExpression="\d{3}-\d{2}-\d{4}"
ControlToValidate="txtSSN">
</asp:RegularExpressionValidator>
</form>
Questa espressione di convalida è tra quelle standard fornite in Visual Studio .NET. Convalida il formato del campo di input fornito nonché il tipo e la lunghezza. L'input deve consistere in tre cifre seguite da un trattino, due cifre seguite da un altro trattino e infine altre quattro cifre.
Se non si utilizzano i controlli nel server (che escludono i controlli di convalida) oppure se è necessario convalidare l'input proveniente da origini diverse dai campi dei form, è possibile utilizzare la classe System.Text.RegularExpression.Regex nel codice del metodo. Nell'esempio seguente è mostrato come convalidare lo stesso campo utilizzando il metodo statico Regex.IsMatch direttamente nella classe della pagina anziché utilizzando un controllo di convalida:
if (!Regex.IsMatch(txtSSN.Text, @"^\d{3}-\d{2}-\d{4}$"))
{
// Invalid Social Security Number
}I campi di input che hanno un tipo .NET Framework equivalente possono essere verificati dal sistema dei tipi .NET Framework. Per convalidare una data, ad esempio, è possibile convertire il valore di input in una variabile del tipo System.DateTime e gestire eventuali eccezioni di formato nel caso in cui i dati dell'input non fossero compatibili, come mostrato di seguito.
try
{
DateTime dt = DateTime.Parse(txtDate.Text).Date;
}
// If the type conversion fails, a FormatException is thrown
catch( FormatException ex )
{
// Return invalid date message to caller
}
Oltre alle verifiche a livello di formato e tipo, potrebbe rendersi necessaria una verifica a livello di intervallo, facilmente eseguibile con la variabile DateTime, come illustrato di seguito.
// Exception handling is omitted for brevity
DateTime dt = DateTime.Parse(txtDate.Text).Date;
// The date must be today or earlier
if ( dt > DateTime.Now.Date )
throw new ArgumentException("Date must be in the past");Se è necessario convalidare dati numerici, ad esempio l'età, eseguire le verifiche del tipo utilizzando il tipo int. Per convertire in un valore Integer l'input della stringa, è possibile utilizzare Int32.Parse o Convert.ToIn32, quindi gestire eventuali FormatException che si verifichino con un tipo dati non valido, come illustrato di seguito:
try
{
int i = Int32.Parse(txtAge.Text);
. . .
}
catch( FormatException)
{
. . .
}È a volte necessario accertarsi che i dati di input siano compresi in un intervallo predeterminato. Nel codice seguente è utilizzato il controllo ASP.NET RangeValidator per limitare l'input a numeri interi compresi tra 0 e 255. Nell'esempio viene utilizzato anche RequiredFieldValidator, senza il quale gli altri controlli di convalida accettano un input vuoto.
<form id="WebForm3" method="post" runat="server">
<asp:TextBox id="txtNumber" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator
id="rangeRegex"
runat="server"
ErrorMessage="Please enter a number between 0 and 255"
ControlToValidate="txtNumber"
style="LEFT: 10px; POSITION: absolute; TOP: 47px" >
</asp:RequiredFieldValidator>
<asp:RangeValidator
id="RangeValidator1"
runat="server"
ErrorMessage="Please enter a number between 0 and 255"
ControlToValidate="TextBox1"
Type="Integer"
MinimumValue="0"
MaximumValue="255"
style="LEFT: 10px; POSITION: absolute; TOP: 47px" >
</asp:RangeValidator>
<asp:Button id="Button1" style="LEFT: 10px; POSITION: absolute; TOP: 100px"
runat="server" Text="Button"></asp:Button>
</form>
Nell'esempio seguente viene mostrato come convalidare l'intervallo utilizzando la classe Regex:
try
{
// The conversion will raise an exception if not valid.
int i = Convert.ToInt32(sInput);
if ((0 <= i && i <= 255) == true)
{
// data is valid, use the number
}
}
catch( FormatException )
{
. . .
}
Attraverso la sterilizzazione è possibile rendere sicuri dati potenzialmente dannosi. È utile quando l'intervallo dell'input consentito non ne garantisce la sicurezza. Può consistere nel rimuovere un valore Null dalla stringa fornita dall'utente o nel far precedere i valori da caratteri escape in modo che vengano considerati valori letterali. Nel caso sia necessario sterilizzare l'input e convertire o rimuovere caratteri di input specifici, servirsi di Regex.Replace.
Nota: utilizzare questo approccio per la difesa a più livelli. Iniziare sempre imponendo all'input di rispettare l'insieme di valori validi noti.
Il codice seguente rimuove una serie di caratteri potenzialmente non sicuri, compresi <>\"'%;()&.
private string SanitizeInput(string input)
{
Regex badCharReplace = new Regex(@"^([<>""'%;()&])$");
string goodChars = badCharReplace.Replace(input, "");
return goodChars;
}
Per ulteriori informazioni sulla sterilizzazione di campi di input a formato libero, come quelli in cui inserire commenti, vedere "Sterilizzazione dell'input a formato libero" nella sezione "Script tra siti" di questo modulo.
Se non si utilizzano i controlli in esecuzione nel server, vale a dire controlli con l'attributo runat="server", e si utilizzano invece i normali controlli HTML, non è possibile avvalersi dei controlli di convalida ASP.NET. È possibile invece convalidare il contenuto delle pagine Web mediante le espressioni regolari nel gestore eventi Page_Load, come segue.
using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// Note that IsPostBack applies only for
// server forms (with runat="server")
if ( Request.RequestType == "POST" ) // non-server forms
{
// Validate the supplied email address
if( !Regex.Match(Request.Form["email"],
@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$",
RegexOptions.None).Success)
{
// Invalid email address
}
// Validate the supplied name
if ( !RegEx.Match(Request.Form["name"],
@"^[A-Za-z'\- ]$",
RegexOptions.None).Success)
{
// Invalid name
}
}
}
Se in base all'input dell'utente vengono generate query SQL dinamiche, un eventuale attacco con inserimento di codice SQL potrebbe inserire comandi SQL dannosi che vengono poi eseguiti dal database. In un classico scenario Web di accesso ai dati può essere utilizzata la seguente strategia di difesa a più livelli:
| • | Utilizzare espressioni regolari per vincolare l'input nella classe della pagina. |
| • | Sterilizzare o respingere l'input. Per la difesa a più livelli è possibile decidere se utilizzare un metodo di supporto per rimuovere i caratteri Null o altri caratteri non validi noti. |
| • | Per avere la certezza che vengano eseguite verifiche a livello di tipo e di lunghezza sui dati contenuti nelle query SQL, utilizzare stored procedure con parametri per l'accesso ai dati. |
Per ulteriori informazioni sull'utilizzo di parametri per l'accesso ai dati e sull'elaborazione di codice di accesso ai dati protetto, vedere il modulo 14 "Creazione di codice di accesso ai dati protetto".
In genere è consigliabile evitare di scrivere codice che accetta l'input da file o da percorsi proveniente dal chiamante ed è invece preferibile utilizzare nomi e posizioni di file fissi durante la lettura e la scrittura dei dati. In tal modo si ha la garanzia che il codice non possa essere forzato ad accedere a file arbitrari e che non sia esposto a errori di normalizzazione.
Se è proprio necessario accettare i nomi di file di input, esistono due difficoltà principali. Innanzitutto, il percorso e il nome del file risultanti sono validi nel contesto del file system? In secondo luogo, il percorso è valido nel contesto dell'applicazione? Ad esempio, è contenuto nella directory principale virtuale dell'applicazione?
Per normalizzare il nome del file, utilizzare System.IO.Path.GetFullPath. Per verificare se il percorso del file è valido nel contesto dell'applicazione, è possibile utilizzare la protezione dall'accesso di codice .NET per concedere l'esatta autorizzazione FileIOPermission al codice affinché questo sia in grado di accedere solo ai file contenuti in directory specifiche. Per ulteriori informazioni, vedere le sezioni relative a I/O di file nel modulo 7 "Creazione di assembly protetti" e nel modulo 8 "Protezione dall'accesso di codice".
Se si utilizza MapPath per mappare un percorso virtuale fornito su un percorso fisico del server, servirsi del sovraccarico di Request.MapPath che accetta un parametro booleano in modo da evitare la mappatura tra applicazioni, come segue:
try
{
string mappedPath = Request.MapPath( inputPath.Text,
Request.ApplicationPath, false);
}
catch (HttpException)
{
// Cross-application mapping attempted
}
L'ultimo parametro false impedisce la mappatura tra applicazioni. Ciò significa che non è consentito fornire un percorso contenente ".." per oltrepassare la gerarchia della directory virtuale dell'applicazione. Qualsiasi tentativo in tal senso genera un'eccezione del tipo HttpException.
Nota: i controlli del server possono avvalersi del metodo Control.MapPathSecure per leggere i file. Tale metodo impone che al codice del chiamante venga concessa l'attendibilità totale dai criteri di protezione dall'accesso di codice; in caso contrario viene generata l'eccezione HttpException. Per ulteriori informazioni, vedere Control.MapPathSecure nella documentazione dell'SDK di .NET Framework.
In Visual Studio .NET sono disponibili alcune espressioni regolari utili per accedere alle quali è necessario aggiungere un controllo RegularExpresssionValidator a un Web form e fare clic sul pulsante con i puntini di sospensione nel campo della proprietà Expression del controllo. Nella tabella seguente sono incluse numerose altre espressioni per i campi utilizzati di frequente nelle pagine Web.
Tabella 10.2 Espressioni regolari utili
| Campo | Espressione | Formato di esempio | Descrizione |
Nome | [a-zA-Z'`-´\s]{1,40} | John DoeO'Dell | Convalida un nome. Consente l'utilizzo di un massimo di 40 caratteri maiuscoli e minuscoli e di alcuni caratteri speciali tipici di alcuni nomi. L'elenco può essere adattato. |
Numeri | ^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$ | (425)-555-0123 | Convalida un numero di telefono utilizzato negli USA. |
Posta elettronica | \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* | Convalida un indirizzo di posta elettronica. | |
URL | ^(http|https|ftp)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*$ |
| Convalida un URL. |
Codice postale | ^(\d{5}-\d{4}|\d{5}|\d{9})$|^([a-zA-Z]\d[a-zA-Z] \d[a-zA-Z]\d)$ |
| Convalida un codice postale utilizzato negli USA ammettendo 5 o 9 cifre. |
Password | ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$ |
| Convalida una password complessa. Deve avere un numero di caratteri compreso tra 8 e 10. Deve contenere una combinazione di lettere maiuscole, minuscole e cifre e non contenere caratteri speciali. |
Interi non negativi | \d+ | 0986 | Convalida numeri interi maggiori di zero. |
Valuta (valori non negativi) | "\d+(\.\d\d)?" |
| Convalida un importo positivo. Richiede due cifre dopo la virgola decimale. |
Valuta (positiva o negativa) | "(-)?\d+(\.\d\d)?" |
| Convalida un importo positivo o negativo. Richiede due cifre dopo la virgola decimale. |
Gli attacchi basati sugli script tra siti sfruttano i punti deboli della convalida delle pagine Web inserendo del codice di script dal lato client. Tale codice viene successivamente inviato a un utente inconsapevole ed eseguito dal browser. Poiché il browser scarica il codice dello script da un sito attendibile, non ha alcun modo per identificarlo come non legittimo e le aree di protezione di Internet Explorer non costituiscono alcuna difesa. Gli attacchi di questo tipo vengono sferrati attraverso connessioni HTTP o HTTPS (SSL). Uno degli attacchi più gravi si verifica quando un pirata informatico scrive uno script per recuperare il cookie di autenticazione che consente di accedere al sito attendibile e lo invia a un indirizzo Web a lui noto. Ciò gli consente di nascondersi dietro l'identità dell'utente legittimo per accedere illegalmente al sito Web.
Per evitare attacchi basati sugli script tra siti, utilizzare le contromisure seguenti:
| • | Convalidare l'input |
| • | Codificare l'output |
Convalidare qualsiasi input proveniente dall'esterno della zona protetta dell'applicazione in quanto a tipo, lunghezza, formato e intervallo servendosi delle varie tecniche già descritte in questo modulo.
Se l'output da inserire nella pagina Web è un testo e non si sa con certezza se esso non contenga caratteri HTML speciali (come <, > e &), avere cura di sottoporlo a una pre-elaborazione con il metodo HttpUtility.HtmlEncode. Eseguire questa misura preventiva anche se il testo è un input utente, se proviene da un database o da un file locale. Allo stesso modo utilizzare HttpUtility.UrlEncode per codificare le stringhe degli URL.
Il metodo HtmlEncode sostituisce i caratteri che hanno un significato speciale in HTML con variabili HTML che li rappresentano. Ad esempio < è sostituito da < e " è sostituito da". I dati codificati non provocano l'esecuzione del codice nel browser bensì diventano dati HTML innocui.
Response.Write(HttpUtility.HtmlEncode(Request.Form["name"]));
I controlli Web con associazione a dati non codificano l'output. L'unico che codifica l'output è il controllo TextBox quando la relativa proprietà TextMode è impostata su MultiLine. Se si associa qualsiasi altro controllo a dati contenenti codice XSS dannoso, questo verrà eseguito nel client. Di conseguenza, se si recuperano dati da un database e non si ha la certezza che siano validi (magari perché il database è condiviso con altre applicazioni), avere cura di codificarli prima di inviarli al client.
Se nella pagina Web è inclusa una casella di testo a formato libero, come può esserlo un campo per i commenti, nella quale si desidera consentire l'utilizzo di alcuni elementi HTML sicuri come i caratteri <b> e <i>, è possibile procedere con sicurezza eseguendo innanzitutto una pre-elaborazione con HtmlEncode e rimuovendo poi selettivamente la codifica dagli elementi interessati, come segue:
StringBuilder sb = new StringBuilder( HttpUtility.HtmlEncode(userInput) ) ;
sb.Replace("<b>", "<b>");
sb.Replace("</b>", "</b>");
sb.Replace("<i>", "<i>");
sb.Replace("</i>", "</I>");
Response.Write(sb.ToString());
Per evitare gli attacchi con script tra siti, utilizzare le contromisure seguenti oltre alle tecniche appena discusse:
| • | Impostare la codifica dei caratteri corretta. |
| • | Utilizzare l'opzione ASP.NET versione 1.1 validateRequest. |
| • | Installare URLScan nel server Web. |
| • | Utilizzare l'opzione HttpOnly per cookie. |
| • | Utilizzare l'attributo di protezione <frame>. |
| • | Utilizzare la proprietà innerText. |
Per circoscrivere la quantità di dati validi per la pagina Web, è importante limitare i modi in cui i dati di input possono essere rappresentati al fine di evitare che gli utenti malintenzionati possano servirsi della normalizzazione e delle sequenze di caratteri escape multibyte per ingannare le routine di convalida dell'input utilizzate.
In ASP.NET è possibile specificare il set di caratteri a livello di pagina o di applicazione utilizzando l'elemento <globalization> in Web.config. Di seguito sono illustrati entrambi gli approcci in cui viene applicata la codifica dei caratteri ISO-8859-1, impostazione predefinita nelle prime versioni di HTML e HTTP.
Per impostare la codifica dei caratteri a livello di pagina, utilizzare l'elemento <meta> oppure l'attributo a livello di pagina ResponseEncoding , come segue:
<meta http-equiv="Content Type"
content="text/html; charset=ISO-8859-1" />
OPPURE
<% @ Page ResponseEncoding="ISO-8859-1" %>
Per impostare la codifica dei caratteri in Web.config, utilizzare la configurazione seguente:
<configuration>
<system.web>
<globalization
requestEncoding="ISO-8859-1"
responseEncoding="ISO-8859-1"/>
</system.web>
</configuration>
Convalida dei caratteri Unicode
Per convalidare i caratteri Unicode contenuti in una pagina, utilizzare il codice seguente:
using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// Name must contain between 1 and 40 alphanumeric characters
// together with (optionally) special characters '`´ for names such
// as D'Angelo
if (!Regex.IsMatch(Request.Form["name"], @"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
throw new ArgumentException("Invalid name parameter");
// Use individual regular expressions to validate other parameters
. . .
}
Segue la spiegazione dell'espressione regolare inclusa nel codice precedente:
| • | {<name>} specifica una classe di caratteri Unicode denominata. |
| • | \p{<name>} è uguale a qualsiasi carattere della classe denominata specificata da {<name>}. |
| • | {L} esegue una corrispondenza da sinistra a destra. |
| • | {Lu} esegue una corrispondenza di maiuscole. |
| • | {Ll} esegue una corrispondenza di minuscole. |
| • | {Zs} è uguale a separatore e spazio. |
| • | {1,40} significa non meno di 1 carattere e non più di 40. |
| • | {Mn} è uguale a caratteri di contrassegno e a spaziatura fissa. |
| • | {Zs} è uguale a separatore e spazio. |
| • | * specifica zero o più corrispondenze. |
| • | $ sospende la ricerca da questa posizione. |
L'attributo validateRequest è una funzione di .NET Framework versione 1.1. È configurato su true per impostazione predefinita nell'elemento <pages> di Machine.config e in ASP.NET comporta la ricerca, in tutti i dati ricevuti dal browser, di input potenzialmente dannoso, ad esempio input contenente elementi <script>. Viene eseguita l'analisi dell'input proveniente da campi di form HTML, cookie e stringhe di query. In .NET Framework versione 1.0 non esistono funzionalità equivalenti, ma il filtro ISAPI (Internet Server Application Programming Interface) URLScan di IIS è in grado di eseguire un'operazione simile. È possibile applicare l'impostazione a ogni pagina con il tag @ Page, come segue:
<% @ Page validateRequest="True" %>
URLScan è un filtro ISAPI che viene installato durante l'esecuzione dello strumento IISLockdown. Consente di attenuare il rischio di attacchi basati su script tra siti respingendo l'input potenzialmente dannoso. Per ulteriori informazioni su IISLockdown e URLScan, vedere il modulo 16 "Protezione del server Web".
Nota: in IIS 6.0 incluso in Windows Server 2003 sono incorporate funzionalità equivalenti a quelle di URLScan.
In Internet Explorer 6 Service Pack 1 è supportato un nuovo attributo per cookie, HttpOnly, che impedisce agli script sul lato client di accedere al cookie dalla proprietà document.cookie. Viene restituita una stringa vuota. Il cookie viene comunque inviato al server quando l'utente visita un sito Web nel dominio corrente.
Nota: i browser Web che non supportano l'attributo di cookie HttpOnly ignorano il cookie o l'attributo, rimanendo così esposti ad attacchi basati su script tra siti.
Attualmente la classe System.Net.Cookie non supporta la proprietà HttpOnly. Per aggiungere la proprietà HttpOnly al cookie, è necessario utilizzare un filtro ISAPI oppure, se si desidera una soluzione con codice gestito, aggiungere il codice seguente al gestore eventi Application_EndRequest dell'applicazione in Global.asax:
protected void Application_EndRequest(Object sender, EventArgs e)
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies)
{
// Just set the HttpOnly attribute on the Forms authentication cookie
// Skip this check to set the attribute on all cookies in the collection
if (sCookie.Equals(authCookie))
{
// Force HttpOnly to be added to the cookie header
Response.Cookies[sCookie].Path += ";HttpOnly";
}
}
}
Nota: in una versione futura di .NET Framework la classe Cookie disporrà probabilmente della proprietà HttpOnly.
In Internet Explorer 6 e versioni successive è supportato un nuovo attributo security negli elementi <frame> e <iframe>. È possibile utilizzare l'attributo security per applicare le impostazioni dell'area di protezione Siti con restrizioni di Internet Explorer dell'utente a un elemento frame o iframe. Per impostazione predefinita l'area Siti con restrizioni non supporta l'esecuzione di script. Se si utilizza l'attributo security, deve essere impostato su "restricted" come mostrato sotto:
<frame security="restricted" src="http://www.somesite.com/somepage.htm"></frame>
Se si crea una pagina con un input non attendibile, utilizzare la proprietà innerText anziché innerHTML. La proprietà innerText protegge il contenuto ed impedisce l'esecuzione dello script.
Un sistema di autenticazione poco efficace aumenta il rischio di spoofing di identità. Se le credenziali di accesso di un utente giungono nelle mani sbagliate, l'eventuale pirata informatico potrebbe falsificare l'identità dell'utente per accedere all'applicazione condividendo con l'utente legittimo tutti i privilegi ad essa relativi. Le credenziali devono essere protette durante la trasmissione in rete e durante la permanenza, ad esempio, nell'archivio utente dell'applicazione. Anche il cookie di autenticazione, che rappresenta un'identità autenticata nell'applicazione dopo il primo accesso, deve essere protetto per attenuare il rischio di dirottamento di sessione e di riproduzione di cookie.
Il pericolo di attacchi con dirottamento di sessione e riproduzione di cookie è particolarmente reale per le applicazioni in cui si utilizza l'autenticazione dei form. È necessario fare particolare attenzione quando si inoltrano query al database utilizzando le credenziali fornite dall'utente, al fine di cautelarsi rispetto a possibile inserimento di codice SQL. Inoltre, per evitare lo spoofing di identità, accertarsi che l'archivio utente sia protetto e che siano utilizzate password complesse.
Nel frammento di codice seguente è indicata una configurazione di autenticazione basata su form sicura in Web.config:
<forms loginUrl="Restricted\login.aspx" Login page in an SSL protected folder
protection="All" Privacy and integrity
requireSSL="true" Prevents cookie being sent over http
timeout="10" Limited session lifetime
name="AppNameCookie" Unique per-application name
path="/FormsAuth" and path
slidingExpiration="true" > Sliding session lifetime
</forms>
Le raccomandazioni seguenti consentono di creare una soluzione sicura per l'autenticazione basata su form:
| • | Partizionare il sito Web. |
| • | Proteggere le pagine con restrizioni con il protocollo SSL. |
| • | Utilizzare l'autorizzazione di URL. |
| • | Proteggere il cookie di autenticazione. |
| • | Utilizzare URL assoluti per gli spostamenti. |
| • | Utilizzare una gestione protetta delle credenziali. |
Nella progettazione del sito avere cura di collocare le pagine protette che richiedono un accesso autenticato in una sottodirectory separata rispetto a quella per le pagine ad accesso anonimo. Nella figura 10.3 è illustrata una disposizione classica visualizzata nella finestra Esplora soluzioni di Visual Studio .NET. Notare come la pagina per la procedura di accesso ai form si trovi in una sottodirectory separata insieme ad altre pagine con restrizioni.

Figura 10.3
Sottodirectory per le pagine con restrizioni che richiedono l'accesso autenticato
Nota: se nell'applicazione si utilizza Server.Transfer per eseguire il passaggio da una pagina anonima a una pagina protetta, ricordare che in .NET Framework 1.1 e versioni precedenti le verifiche di autenticazione sono ignorate pertanto è necessario verificare il codice contenente Server.Transfer per avere la certezza che non consenta il trasferimento a una directory protetta.
Per avere la certezza che venga utilizzato il protocollo SSL per proteggere le credenziali provenienti da un form di accesso e il cookie di autenticazione inviato a richieste successive di accesso a pagine con restrizioni, configurare le cartelle protette in IIS in modo che lo richiedano. In tal modo l'attributo AccessSSL=true viene impostato per la cartella nella metabase di IIS. Le richieste di accesso alle pagine contenute nelle cartelle protette verranno soddisfatte solo se nell'URL verrà utilizzato https.
Per SSL è necessario che nel server Web sia installato un certificato server. Per ulteriori informazioni, vedere "How To: Setup SSL on a Web Server" nella sezione "How To" di "Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication" all'indirizzo http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp (in inglese).
Per consentire l'accesso anonimo a pagine pubbliche, utilizzare il seguente elemento <authorization>.
<system.web>
<!-- The virtual directory root folder contains general pages.
Unauthenticated users can view them and they do not need
to be secured with SSL. -->
<authorization>
<allow users="*" />
</authorization>
</system.web>
In Web.config utilizzare il seguente elemento <authorization> in un elemento <location> per negare l'accesso agli utenti non autenticati e reindirizzarli automaticamente alla pagina di accesso specificata nell'elemento <forms>:
<!-- The restricted folder is for authenticated and SSL access only. -->
<location path="Secure" >
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>Per evitare attacchi con dirottamento di sessione e riproduzione di cookie, proteggere il cookie avendo cura che possa essere trasmesso solo attraverso connessioni SSL con il protocollo HTTPS. Per ridurre ulteriormente eventuali rischi, crittografare il cookie prima di inviarlo al client e applicare una scadenza alla sua validità. Per proteggere il cookie di autenticazione:
| • | Limitare la trasmissione del cookie di autenticazione alle connessioni HTTPS. |
| • | Crittografare il cookie. |
| • | Limitare la durata del cookie. |
| • | Valutare l'opportunità di utilizzare una scadenza prefissata. |
| • | Non utilizzare cookie di autenticazione permanenti. |
| • | Tenere separati i cookie di autenticazione da quelli di rappresentazione. |
| • | Utilizzare nomi e percorsi specifici per i cookie. |
Nei cookie è prevista una proprietà di protezione che stabilisce se il browser può rinviarlo al server. Se tale proprietà di protezione è impostata, il cookie viene inviato dal browser solo a una pagina protetta richiesta mediante un URL HTTPS.
Se si utilizza .NET Framework versione 1.1, impostare la proprietà di protezione utilizzando requireSSL="true" nell'elemento <forms> , come segue:
<forms loginUrl="Secure\Login.aspx"
requireSSL="true" . . . />
Se si utilizza .NET Framework versione 1.0, impostare la proprietà di protezione manualmente nel gestore eventi Application_EndRequest di Global.asax applicando il codice seguente:
protected void Application_EndRequest(Object sender, EventArgs e)
{
string authCookie = FormsAuthentication.FormsCookieName;
foreach (string sCookie in Response.Cookies)
{
if (sCookie.Equals(authCookie))
{
// Set the cookie to be secure. Browsers will send the cookie
// only to pages requested with https
Response.Cookies[sCookie].Secure = true;
}
}
}
Crittografare i contenuti del cookie anche se si utilizza SSL. Nell'eventualità che il pirata informatico riesca ad impossessarsi del cookie durante un attacco XSS, non potrà così vederlo né modificarlo, anche se rimarrà in grado di utilizzarlo per accedere all'applicazione. Il modo migliore per ridurre tale rischio è di implementare le contromisure appropriate per evitare gli attacchi XSS (come già descritto in "Script tra siti") e di limitare la durata del cookie come dettagliato nella raccomandazione seguente.
Per garantire la riservatezza e l'integrità dei dati del cookie, impostare l'attributo protection dell'elemento <forms> come segue:
<forms protection="All" Privacy and integrity
Limitando la durata del cookie viene ridotto l'intervallo di tempo durante il quale il pirata informatico può servirsi di un cookie per accedere all'applicazione sotto mentite spoglie.
<forms timeout="10" Reduced cookie lifetime (10 minutes)
Valutare l'opportunità di impostare slidingExpiration="false" nell'elemento <forms> per stabilire la scadenza del cookie, anziché reimpostare la scadenza dopo ogni richiesta Web. È questa una misura particolarmente importante se non si utilizza SSL per proteggere il cookie.
Nota: questa funzione è disponibile in .NET Framework versione 1.1.
Non utilizzare cookie di autenticazione permanenti perché questi vengono memorizzati nel profilo dell'utente e possono essere sottratti se un pirata informatico riesce ad accedere fisicamente al computer dell'utente. Quando si crea FormsAuthenticationTicket, è possibile specificare un cookie non permanente come segue:
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(
1, // version
Context.User.Identity.Name, // user name
DateTime.Now, // issue time
DateTime.Now.AddMinutes(15), // expires every 15 mins
false, // do not persist the cookie
roleStr ); // user roles
I cookie di rappresentazione, che contengono le preferenze degli utenti e dati non riservati, devono essere tenuti separati dai cookie di autenticazione. I cookie di rappresentazione, se sottratti, non costituiscono un rischio per la protezione mentre i cookie di autenticazione consentono al pirata informatico di accedere all'applicazione.
Utilizzare valori univoci per gli attributi name e path nell'elemento <forms>. Così facendo si evitano i problemi che potrebbero verificarsi quando più applicazioni sono in esecuzione nello stesso server. Se non si utilizzano nomi specifici, ad esempio, è possibile che l'utente autenticato in un'applicazione invii una richiesta a un'altra applicazione senza essere rinviato alla pagina di accesso di quest'ultima.
Per ulteriori informazioni, vedere gli articoli della Microsoft Knowledge Base 313116 "PRB: Forms Authentication Requests Are Not Directed to loginUrl Page" e 310415 "PRB: Mobile Forms Authentication and Different Web Applications" (in inglese).
Gli spostamenti tra l'area pubblica e quella con restrizioni del sito, vale a dire tra pagine HTTP e HTTPS, è un problema perché il reindirizzamento utilizza sempre il protocollo, HTTPS o HTTP, della pagina corrente anziché della pagina di destinazione.
Quando un utente visualizza ed esplora pagine contenute in una directory protetta con SSL, i collegamenti relativi, come "..\paginapubblica.aspx" o i reindirizzamenti a pagine HTTP, vengono serviti dal protocollo https che in tal modo subisce un sovraccarico inutile. Per evitare tale circostanza, utilizzare collegamenti assoluti come "http://nomeserver/nomeapplicazione/paginapubblica.aspx" quando si esegue il reindirizzamento da una pagina HTTPS a una pagina HTTP.
Allo stesso modo, quando si reindirizza a una pagina protetta (ad esempio la pagina di accesso) da un'area pubblica del sito, utilizzare un percorso HTTPS assoluto, come "https://nomeserver/nomeapplicazione/protetto/accesso.aspx", anziché un percorso relativo, come limitato/accesso.aspx. Se, ad esempio, nella pagina Web è disponibile un pulsante per l'accesso, utilizzare il codice seguente per reindirizzare alla pagina di accesso protetta:
private void btnLogon_Click( object sender, System.EventArgs e )
{
// Form an absolute path using the server name and v-dir name
string serverName =
HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);
string vdirName = Request.ApplicationPath;
Response.Redirect("https://" + serverName + vdirName +
"/Restricted/Login.aspx");
}Lo spoofing (falsificazione) dell'identità è uno dei rischi a livello di autenticazione più frequenti per le applicazioni. Si verifica quando un pirata informatico accede all'applicazione sotto le mentite spoglie di un altro utente. Un modo per riuscire in questo intento consiste nell'impossessarsi del cookie di sessione, ma se il cookie di autenticazione viene protetto come descritto in precedenza, il rischio viene notevolmente ridotto. È inoltre necessario creare una gestione protetta delle credenziali e un archivio utente protetto per attenuare il rischio rappresentato dagli attacchi alle password di tipo "brute force", gli attacchi al dizionario e quelli con inserimento di codice SQL.
Le raccomandazioni seguenti consentono di ridurre il rischio:
| • | Utilizzare hash unidirezionali per le password. |
| • | Utilizzare password complesse. |
| • | Evitare l'inserimento di SQL. |
Se per l'archivio utente si utilizza SQL Server, memorizzare i digest di password unidirezionali (valori hash) aggiungendovi un valore salt casuale. Il valore salt aggiunto riduce la possibilità che la password subisca violazioni di tipo "brute force", come negli attacchi al dizionario. Con il digest le password non vengono mai effettivamente memorizzate, bensì vengono recuperate dall'archivio utente e convalidate ricalcolando il digest e confrontandolo con il valore memorizzato.
Per avere la certezza che le password degli utenti siano conformi a linee guida per password complesse, utilizzare le espressioni regolari. L'espressione regolare riportata di seguito garantisce la composizione di password contenenti da 8 a 10 caratteri e una combinazione di caratteri maiuscoli, minuscoli, numerici e speciali. Si tratta di un'ulteriore misura che riduce il rischio di attacchi al dizionario.
private bool IsStrongPassword( string password )
{
}A causa del modo in cui le credenziali di accesso fornite dall'utente vengono utilizzate per inviare query al database, l'autenticazione dei form è particolarmente esposta ad attacchi con inserimento di codice SQL. Per attenuare questo rischio:
| • | Convalidare attentamente le credenziali fornite. Utilizzare espressioni regolari per fare in modo che non comprendano caratteri SQL. |
| • | Utilizzare stored procedure con parametri per accedere al database dell'archivio utente. |
| • | Utilizzare un accesso al database con restrizioni e privilegi minimi. |
Per ulteriori informazioni sulle procedure che consentono di evitare l'inserimento di SQL, consultare il modulo 14 "Creazione di codice di accesso ai dati protetto".
L'autorizzazione consente di controllare l'accesso alle directory, alle singole pagine Web, alle classi delle pagine e ai metodi. Se necessario, è anche possibile includere la logica dell'autorizzazione nel codice del metodo. Quando si inserisce l'autorizzazione nelle pagine e nei controlli Web sviluppati, tenere conto delle raccomandazioni seguenti:
| • | Utilizzare l'autorizzazione di URL per il controllo dell'accesso a pagine e directory. |
| • | Utilizzare l'autorizzazione di file con l'autenticazione di Windows. |
| • | Utilizzare richieste Principal in classi e metodi. |
| • | Utilizzare verifiche dei ruoli esplicite per l'autorizzazione specifica. |
Per il controllo dell'accesso a livello di pagina e di directory, utilizzare l'autorizzazione di URL, configurata dall'elemento <authorization>. Per limitare l'accesso a file o directory specifici, inserire l'elemento <authorization> nell'elemento <location>.
Per ulteriori informazioni vedere la sezione "Autorizzazione" del modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web".
Se ASP.NET è configurato per utilizzare l'autenticazione di Windows, FileAuthorizationModule verifica tutte le richieste a fronte dei tipi di file ASP.NET, compresi i file di pagine ASP.NET (.aspx), i controlli utente (.ascx) e qualsiasi altro tipo di file mappato da IIS sul filtro ISAPI ASP.NET.
Per configurare FileAuthorizationModule, impostare gli appositi elenchi di controllo di accesso Windows (ACL, Access Control List) nei file ASP.NET.
Le richieste di autorizzazione per l'oggetto Principal consentono di prendere decisioni a livello di autorizzazione in base all'identità del chiamante e alla sua appartenenza a ruoli. L'identità e l'appartenenza a ruoli del chiamante sono gestiti dall'oggetto Principal associato alla richiesta Web corrente (a cui si accede tramite HttpContext.User). Utilizzare attributi di protezione dichiarativi per fornire controlli di accesso a classi e metodi, come segue:
// Declarative syntax
[PrincipalPermission(SecurityAction.Demand,
Role=@"DomainName\WindowsGroup")]
public void SomeRestrictedMethod()
{
}Le verifiche di protezione dichiarative impediscono agli utenti di accedere a una classe o di richiamare un metodo specifico. Se per prendere decisioni a livello di autorizzazione è necessario aggiungere altra logica, utilizzare richieste di autorizzazione per l'oggetto Principal imperative oppure verifiche dei ruoli esplicite servendosi di IPrincipal.IsInRole. Entrambi gli approcci consentono di utilizzare altre variabili runtime per ottimizzare la decisione relativa all'autorizzazione. Nell'esempio seguente è illustrato l'utilizzo di una richiesta di autorizzazione per l'oggetto Principal di tipo imperativo:
// Imperative syntax
public void SomeRestrictedMethod()
{
// Only callers that are members of the specified Windows group
// are allowed access
PrincipalPermission permCheck = new PrincipalPermission(
null, @"DomainName\WindowsGroup");
permCheck.Demand();
// Some restricted operations (omitted)
}
Nell'esempio seguente è mostrato l'utilizzo di IPrincipal.IsInRole:
public void TransferMoney( string fromAccount,
string toAccount, double amount)
{
// Extract the authenticated user from the current HTTP context.
// The User variable is equivalent to HttpContext.Current.User if you
// are using an .aspx page (or .asmx)
WindowsPrincipal authenticatedUser = User as WindowsPrincipal;
if (null != authenticatedUser)
{
// Note: To retrieve the authenticated user's username, use the
// following line of code
// string username = authenticatedUser.Identity.Name;
// If the amount exceeds a threshold value, manager approval is required
if (amount > thresholdValue) {
// Perform a role check
if (authenticatedUser.IsInRole(@"DomainName\Manager") )
{
// OK to proceed with transfer
}
else
{
throw new Exception("Unauthorized funds transfer");
}
}
else
{
. . .
}
}
}
È inoltre possibile avere un metodo che ammette l'esistenza di chiamanti provenienti da vari ruoli diversi. È tuttavia possibile che in seguito si desideri richiamare un metodo diverso, e ciò non è possibile in presenza della protezione dichiarativa.
Per impostazione predefinita, nelle applicazioni ASP.NET in genere non viene eseguita la rappresentazione del chiamante originale per ragioni di progettazione, implementazione e scalabilità. La rappresentazione, ad esempio, impedisce la realizzazione di un pool di connessioni intermedie efficace, circostanza che può incidere negativamente sulla scalabilità dell'applicazione.
In alcuni casi la rappresentazione può essere necessaria, ad esempio se si richiede un'identità alternativa (non di processo) per l'accesso alle risorse. Negli ambienti host le identità anonime multiple sono spesso utilizzate come forma di isolamento dell'applicazione. Se, ad esempio, nell'applicazione si utilizza l'autenticazione basata su form o Passport, è possibile rappresentare l'account utente Internet associato da IIS con la directory virtuale dell'applicazione.
È possibile rappresentare il chiamante originale, che può essere l'account utente Internet anonimo o un'identità fissa. Per rappresentare il chiamante originale (l'identità autenticata da IIS), utilizzare la configurazione seguente:
<identity impersonate="true" />
Per rappresentare un'identità fissa, utilizzare altri attributi userName e password nell'elemento <identity> ma avere cura di archiviare le credenziali crittografate nel Registro di sistema mediante Aspnet_setreg.exe. Per ulteriori informazioni sulle credenziali di crittografia nei file di configurazione e su Aspnet_setreg.exe, vedere il modulo 19 "Protezione dell'applicazione ASP.NET e dei Servizi Web".
Se non si desidera rappresentare un account per l'intera richiesta, è possibile utilizzare la rappresentazione a livello di codice per limitare la rappresentazione a una parte della richiesta. Si desidera ad esempio utilizzare l'account di processi ASP.NET per accedere alle risorse principali dell'applicazione e al database a valle ma è anche necessario accedere a un'altra risorsa, come un database remoto o una condivisione file remota, con un'identità alternativa.
A tal fine, utilizzare IIS per configurare l'account utente anonimo come identità alternativa attendibile. Con il codice seguente creare quindi un token di rappresentazione che utilizzi l'account anonimo solo durante l'esecuzione del codice per l'accesso alla risorsa remota:
HttpContext context = HttpContext.Current;
// Get the service provider from the context
IServiceProvider iServiceProvider = context as IServiceProvider;
//Get a Type which represents an HttpContext
Type httpWorkerRequestType = typeof(HttpWorkerRequest);
// Get the HttpWorkerRequest service from the service provider
// NOTE: When trying to get a HttpWorkerRequest type from the HttpContext
// unmanaged code permission is demanded.
HttpWorkerRequest httpWorkerRequest =
iServiceProvider.GetService(httpWorkerRequestType) as HttpWorkerRequest;
// Get the token passed by IIS
IntPtr ptrUserToken = httpWorkerRequest.GetUserToken();
// Create a WindowsIdentity from the token
WindowsIdentity winIdentity = new WindowsIdentity(ptrUserToken);
// Impersonate the user
Response.Write("Before impersonation: " +
WindowsIdentity.GetCurrent().Name + "<br>");
WindowsImpersonationContext impContext = winIdentity.Impersonate();
Response.Write("Impersonating: " + WindowsIdentity.GetCurrent().Name + "<br>);
// Place resource access code here
// Stop impersonating
impContext.Undo();
Response.Write( "After Impersonating: " +
WindowsIdentity.GetCurrent().Name + "<br>");
Nota: questo approccio presuppone l'esistenza dell'autenticazione basata su form o Passport laddove la directory virtuale dell'applicazione è configurata in IIS per il supporto dell'accesso anonimo.
Se si utilizza questo codice, applicare la configurazione <identity> seguente:
<identity impersonate="false" />
Nota: il codice richiede l'autorizzazione al codice non gestito SecurityPermission(SecurityPermissionFlag.UnmanagedCode), concesso solo ad applicazioni Web pienamente attendibili.
Per dati sensibili si intendono informazioni dettagliate relative alla configurazione dell'applicazione (ad esempio stringhe di connessione e credenziali degli account di servizio) e all'applicazione stessa (ad esempio numeri di carta di credito dei clienti). Le raccomandazioni seguenti consentono di ridurre il rischio in caso di dati sensibili:
| • | Non trasferire i dati sensibili da una pagina all'altra. |
| • | Evitare le password in testo non crittografato nei file di configurazione. |
| • | Utilizzare DPAPI per evitare la gestione delle chiavi. |
| • | Disattivare la cache dell'output per i dati sensibili. |
Evitare di utilizzare le opzioni di gestione dello stato sul lato client, come lo stato di visualizzazione, i cookie, le stringhe di query o le variabili di campi di form nascosti, per archiviare dati riservati. I dati potrebbero subire una manomissione e venire visualizzati come testo in chiaro. Servirsi delle opzioni di gestione dello stato sul lato server, come un database SQL Server, per uno scambio sicuro dei dati.
Gli elementi <processModel>, <sessionState>e <identity> in Machine.config e Web.config presentano gli attributi userName e password. Non archiviarli in testo normale. Archiviare le credenziali crittografate nel Registro di sistema con lo strumento Aspnet_setreg.exe.
Per ulteriori informazioni sulle credenziali di crittografia nei file di configurazione e su Aspnet_setreg.exe, vedere il modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web".
DPAPI è l'ideale per crittografare dati segreti come le stringhe di connessione e le credenziali degli account di servizio. Se le pagine da sviluppare richiedono questo tipo di dati di configurazione, servirsi di DPAPI per evitare il problema della gestione delle chiavi.
Per ulteriori informazioni, vedere la sezione relativa alla crittografia nel modulo 7 "Creazione di assembly protetti".
Se la pagina contiene dati sensibili, come una password, i numeri di carta di credito o lo stato di un account, non memorizzarla nella cache. Per disattivare la cache per una pagina specifica, utilizzare il seguente attributo a livello di pagina:
<%@ Page OutputCache Duration="0" Location="None" VaryByParam="None" %>
Per avere una gestione protetta delle sessioni è necessario tenere conto di due fattori principali. Innanzitutto fare in modo che il token di sessione non possa essere utilizzato per accedere a pagine riservate in cui vengono eseguite operazioni protette o a dati sensibili. In secondo luogo, se i dati di sessione contengono elementi sensibili, è necessario proteggerli, archivio di sessione incluso.
I due tipi di token seguenti sono associati alla gestione delle sessioni:
| • | Token di sessione. Viene generato automaticamente da ASP.NET se è attivato lo stato sessione, ad esempio impostando l'attributo mode dell'elemento <sessionState> su InProc, SQLServer o StateServer. Nota: è possibile ignorare la configurazione <sessionState> e disattivare o attivare lo stato sessione pagina per pagina utilizzando l'attributo EnableSessionState nel tag @Page. |
| • | Token di autenticazione. Viene generato dai meccanismi di autenticazione, come l'autenticazione basata su form, per risalire alla sessione di un utente autenticato. Un token di autenticazione valido consente all'utente di accedere alle pagine con restrizioni del sito Web. |
Le raccomandazioni seguenti consentono di usufruire di una gestione protetta delle sessioni:
| • | Richiedere l'autenticazione per le pagine contenenti dati sensibili. |
| • | Non fare affidamento sulle opzioni di gestione dello stato sul lato client. |
| • | Non confondere token di sessione e token di autenticazione. |
| • | Utilizzare efficacemente il protocollo SSL. |
| • | Proteggere i dati di sessione. |
Avere cura di autenticare gli utenti prima di concedere loro l'accesso a parti del sito contenenti dati sensibili o sottoposte a restrizioni. Se l'autenticazione è protetta e il relativo token è anch'esso protetto con SSL, la sessione dell'utente non può essere violata perché il pirata informatico non potrà dirottare e riprodurre il token di sessione. Per oltrepassare il muro dell'autorizzazione, il pirata informatico ha bisogno del token di autenticazione.
Per ulteriori informazioni sulla protezione del token di autenticazione per l'autenticazione basata su form, vedere la sezione "Autenticazione dei form" già descritta in questo modulo.
Evitare di utilizzare le opzioni di gestione dello stato sul lato client, come lo stato di visualizzazione, i cookie, le stringhe di query o campi di form nascosti, per archiviare dati riservati. Le informazioni potrebbero subire una manomissione e venire visualizzate come testo in chiaro. Per archiviare dati sensibili, utilizzare le opzioni di gestione dello stato sul lato server, ad esempio un database.
Una gestione protetta delle sessioni impone che i due tipi di token non vengano confusi. Innanzitutto proteggere il token di autenticazione per evitare che un pirata informatico possa impossessarsene e utilizzarlo per accedere alle aree con restrizioni dell'applicazione. In secondo luogo creare l'applicazione in modo tale che il token di sessione da solo non possa essere utilizzato per accedere a pagine o dati sensibili. Il token di sessione deve essere utilizzato solo a fini di rappresentazione o per gestire lo stato dell'utente tra molteplici richieste HTTP. Senza autenticazione, non gestire informazioni riservate dello stato dell'utente.
Se il sito presenta aree protette e aree pubbliche, proteggere le prime con SSL. Quando l'utente esegue frequenti spostamenti tra l'area protetta e quella pubblica, il cookie di sessione generato da ASP.NET (o l'URL, se è stato attivato lo stato sessione senza cookie) si sposta anch'esso come testo in chiaro mentre il cookie di autenticazione non viene mai trasmesso attraverso connessioni HTTP non crittografate se la proprietà Secure è impostata.
Nota: è possibile configurare la proprietà Secure di un cookie per l'autenticazione basata su form impostando requireSSL="true" nell'elemento <forms>.
Un pirata informatico è in grado di impossessarsi di un cookie di sessione trasmesso durante una sessione HTTP non crittografata ma se il sito è stato progettato correttamente e le pagine con restrizioni e le risorse sono collocate in una directory separata e protetta egli potrà servirsene solo per accedere alle pagine pubbliche non protette. In tal caso non sussistono problemi di protezione perché in queste pagine non vengono eseguite operazioni a carattere riservato. Quando il pirata informatico tenta di riprodurre il token di sessione per accedere a una pagina protetta, viene reindirizzato alla pagina di accesso dell'applicazione perché non esiste alcun token di autenticazione.
Per ulteriori informazioni sulla proprietà Secure dei cookie e sulla creazione di soluzioni protette per l'autenticazione basata su form, vedere la sezione "Autenticazione dei form" già descritta in questo modulo.
Se i dati di sessione archiviati nel server contengono elementi riservati, proteggere tanto i dati quanto il relativo archivio. In ASP.NET è disponibile il supporto per varie modalità dello stato di sessione. Per informazioni sulla protezione dello stato di sessione ASP.NET, vedere "Stato sessione" nel modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web".
I parametri, come quelli che si trovano in campi di form, in stringhe di query, nello stato di visualizzazione e nei cookie, possono essere manipolati dai pirati informatici che solitamente intendono accedere all'applicazione o indurla a eseguire un'operazione non autorizzata.
Se, ad esempio, un pirata informatico è a conoscenza dell'esistenza di uno schema poco efficace per i token di autenticazione, come un numero facile da indovinare all'interno di un cookie, può costruire un cookie con un altro numero e avanzare una richiesta come se fosse un utente diverso, forse addirittura con privilegi.
Le raccomandazioni seguenti consentono di evitare i problemi correlati alla manipolazione dei parametri:
| • | Proteggere lo stato di visualizzazione con codici di autenticazione dei messaggi. |
| • | Utilizzare Page.ViewStateUserKey per contrastare attacchi con un clic. |
| • | Conservare i dati riservati nel server. |
| • | Convalidare i parametri dell'input. |
Se nelle pagine o nei controlli Web è utilizzato lo stato di visualizzazione per gestire lo stato tra le richieste HTTP, servirsi dei codici di autenticazione dei messaggi (MAC, Message Authentication Code) per accertarsi che sia crittografato e sottoposto a verifica di integrità. Per impostazione predefinita, l'attributo enableViewStateMac nell'elemento <pages> in Machine.config garantisce la protezione dello stato di visualizzazione con un codice di autenticazione dei messaggi.
<pages buffer="true" enableSessionState="true"
enableViewState="true" enableViewStateMac="true"
autoEventWireup="true" validateRequest="true"/>
Nota: l'istruzione @Page supporta anche gli attributi precedenti, consentendo di personalizzare le impostazioni pagina per pagina.
Mentre non è particolarmente importante se lo stato di visualizzazione è attivato in base al singolo controllo, alla singola pagina o applicazione, avere cura di impostare enableViewStateMac su true ogni volta che si utilizza lo stato di visualizzazione.
Se nell'applicazione viene utilizzato Server.Transfer come illustrato sotto e viene impostato su true il secondo parametro booleano facoltativo in modo tale che le raccolte QueryString e Form vengano conservate, il comando non sortirà l'effetto previsto se enableViewStateMac è impostato su true.
Server.Transfer("page2.aspx", true);
Se si omette il secondo parametro o lo si imposta su false, non si verificherà alcun errore. Se si desidera conservare le raccolte QueryString e Form anziché impostare enableViewStateMac su false, attenersi alla soluzione descritta nell'articolo 316920 della Microsoft Knowledge Base "PRB: "View State Is Invalid" Error Message When You Use Server.Transfer" (in inglese).
Per informazioni sulla configurazione dell'elemento <machineKey> relativamente alla verifica della crittografia e dell'integrità dello stato di visualizzazione, vedere il modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web".
Se si autenticano i chiamanti e si utilizza lo stato di visualizzazione, impostare la proprietà Page.ViewStateUserKey nel gestore eventi Page_Init in modo da evitare gli attacchi con un clic. Questi si verificano quando un pirata informatico crea una pagina Web precompilata (.htm o .aspx) con stato di visualizzazione. Lo stato di visualizzazione può essere generato da una pagina che il pirata abbia precedentemente creato, ad esempio la pagina di un carrello della spesa con 100 elementi. Il pirata inviata un utente inconsapevole a visitare la pagina e fa sì che questa venga inviata al server dove lo stato di visualizzazione è valido. Nel server non esiste alcun mezzo per risalire al pirata informatico come origine dello stato di visualizzazione. La convalida dello stato di visualizzazione e i MAC non contrastano l'attacco perché lo stato di visualizzazione è valido e la pagina viene eseguita nel contesto di protezione dell'utente.
Impostare la proprietà Page.ViewStateUserKey su un valore univoco come contromisura all'attacco con un clic. Il valore deve essere univoco per ogni utente e solitamente coincide con il nome utente o l'ID. Quando il pirata informatico crea lo stato di visualizzazione, la proprietà ViewStateUserKey viene inizializzata sul suo nome. Quando l'utente inoltra la pagina al server, essa viene inizializzata con il nome del pirata provocando un errore nella verifica MAC dello stato di visualizzazione e generando una condizione di eccezione.
Nota: poiché nelle pagine ad accesso anonimo, ovvero pagine in cui non è disponibile alcun nome utente, non avviene nessuna transazione a carattere riservato, questo tipo di attacco di solito non costituisce un problema.
Non fare affidamento sui parametri dell'input, soprattutto quando vengono utilizzati per prendere decisioni a livello di protezione nel server. Non utilizzare, inoltre, parametri con testo in chiaro per nessun tipo di informazioni sensibili bensì archiviare i dati sensibili nel server in un archivio di sessione e servirsi di un token di sessione per farvi riferimento. Accertarsi che l'utente sia autenticato in modo sicuro e che il token di autenticazione sia protetto adeguatamente. Per ulteriori informazioni, vedere la sezione "Gestione delle sessioni" già discussa in questo modulo.
Convalidare tutti i parametri dell'input provenienti da campi di form, stringhe di query, cookie e intestazioni HTTP. La classe System.Text.RegularExpressions.Regex consente di convalidare i parametri dell'input. Nel codice seguente, ad esempio, viene mostrato come utilizzare questa classe per convalidare un nome trasmesso attraverso un parametro di stringa di query. La stessa tecnica può essere applicata per convalidare altre forme di parametri di input, ad esempio provenienti da cookie o da campi di form. Per convalidare un parametro di cookie, ad esempio, utilizzare Request.Cookies anziché Request.QueryString.
using System.Text.RegularExpressions;
. . .
private void Page_Load(object sender, System.EventArgs e)
{
// Name must contain between 1 and 40 alphanumeric characters
// together with (optionally) special characters '`´ for names such
// as D'Angelo
if (!Regex.IsMatch(Request.QueryString["name"],
@"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
throw new Exception("Invalid name parameter");
// Use individual regular expressions to validate all other
// query string parameters
. . .
}
Per ulteriori informazioni sulle espressioni regolari e la convalida dei dati dell'input, vedere la sezione "Convalida dell'input" già discussa in questo modulo.
Una corretta gestione delle eccezioni nelle pagine Web consente di evitare che informazioni dettagliate e sensibili contenute nelle eccezioni vengano rivelate all'utente. Le raccomandazioni seguenti si applicano a pagine e controlli ASP.NET.
| • | Restituire al client pagine di errore generiche. |
| • | Implementare gestori degli errori a livello di pagina o di applicazione. |
Per ulteriori informazioni sulla gestione delle eccezioni, vedere il modulo 7 "Creazione di assembly protetti".
Nel caso di un'eccezione non gestita, vale a dire un'eccezione che giunge ai confini dell'applicazione, restituire all'utente una pagina di errore generica. A tal fine configurare come segue l'elemento <customErrors>:
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
Nella pagina di errore dovrebbe essere contenuto un messaggio di errore adeguatamente generico con qualche ulteriore dettaglio di supporto. Il nome della pagina che ha generato l'errore viene comunicato alla pagina di errore mediante il parametro di query aspxerrorpath.
È inoltre possibile utilizzare varie pagine di errore per tipi di errore diversi, ad esempio:
<customErrors mode="On" defaultRedirect="YourErrorPage.htm"> <error statusCode="404" redirect="YourNotFoundPage.htm"/> <error statusCode="500" redirect="YourInternalErrorPage.htm"/> </customErrors>
Il seguente attributo a livello di pagina consente di fornire una pagina di errore per singole pagine:
<% @ Page ErrorPage="YourErrorPage" %>
Se è necessario catturare ed elaborare le eccezioni non gestite a livello di pagina, creare un gestore per l'evento Page_Error analogo a quello mostrato sotto.
public void Page_Error(object sender,EventArgs e)
{
// Get the source exception details
Exception ex = Server.GetLastError();
// Write the details to the event log for diagnostics
. . .
// Prevent the exception from propagating and generating an
// application level event (Application.Error)
Server.ClearError();
}
Se le eccezioni riescono a giungere al gestore di pagina o se non esiste alcun gestore di pagina, viene generato un evento errore di applicazione. Per catturare eventi a livello di applicazione, implementare Application_Error in Global.asax come segue:
protected void Application_Error(Object sender, EventArgs e)
{
// Write to the event log.
}L'identità di processo ASP.NET predefinita per applicazioni Web è in grado di scrivere nuovi record nel registro eventi ma non dispone di autorizzazioni sufficienti per creare nuove origini di evento. Per risolvere il problema esistono due soluzioni possibili. È possibile creare una classe Installer, richiamata al momento dell'installazione quando sono disponibili i privilegi dell'amministratore, oppure configurare le autorizzazioni nella chiave del Registro di sistema EventLog per consentire all'identità di processo ASP.NET (o all'identità rappresentata) di creare origini di evento in fase di esecuzione. È consigliato il primo approccio.
| • | Per creare un'origine di evento dell'applicazione in fase di installazione
|
In presenza di un'applicazione esistente e se non si desidera creare una classe Installer, è necessario concedere all'identità di processo ASP.NET i diritti di accesso corretti nella chiave del Registro di sistema relativa al registro eventi. Per informazioni sulle chiavi del Registro di sistema e i diritti di accesso corretti necessari, vedere la sezione relativa al registro eventi nel modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web".
Al codice che scrive nel registro eventi è necessario concedere l'autorizzazione EventLogPermission mediante criteri di protezione dall'accesso di codice. Ciò può costituire un problema se l'applicazione Web è configurata per essere eseguita a un livello con attendibilità parziale. Per informazioni sulla modalità di scrittura nel registro eventi da parte di un'applicazione Web con attendibilità parziale, vedere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET".
Nella parte iniziale di questo modulo sono stati descritti i pericoli principali da considerare durante la creazione di pagine e controlli Web. Molti attacchi a livello di applicazione sfruttano i punti deboli della convalida dell'input. Prestare un'attenzione speciale al riguardo e sviluppare una strategia di convalida efficace per avere la certezza che tutti i dati elaborati provenienti da un'origine non attendibile siano adeguatamente convalidati. Un'altra vulnerabilità frequente consiste nell'incapacità di proteggere i cookie di autenticazione. Nella sezione "Autenticazione dei form" sono state elencate alcune contromisure efficaci che consentono di evitare gli attacchi basati sull'accesso non autorizzato, il dirottamento di sessione e la riproduzione di cookie.
Per ulteriori informazioni, vedere le risorse seguenti:
| • | Per ulteriori informazioni sulla modalità di configurazione protetta di Machine.config e Web.config, vedere il modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web". |
| • | Per un elenco di controllo stampabile, vedere "Elenco di controllo: Protezione di ASP.NET" nella sezione "Elenchi di controllo" di questa guida. |
| • | Per informazioni sulla protezione della workstation di sviluppo, vedere "Procedura: protezione delle workstation degli sviluppatori" nella sezione "Procedure" di questa guida. |
| • | Per ulteriori informazioni sull'autenticazione e l'autorizzazione in ASP.NET, vedere il modulo 8 "ASP.NET Security," in "Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication" all'indirizzo http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetch08.asp (in inglese). |
| • | Per procedure dettagliate sull'utilizzo dell'autenticazione basata su form, vedere "How To: Use Forms Authentication with SQL Server 2000" e "How To: Use Forms Authentication with Active Directory", nella sezione "How To" di "Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication" all'indirizzo http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT00.asp (in inglese). |
| • | Per ulteriori informazioni sulle espressioni regolari, vedere l'articolo della Microsoft Knowledge Base 308252 "How To: Match a Pattern by Using Regular Expressions and Visual C# .NET" (in inglese). |
| • | Per ulteriori informazioni sulla convalida dell'input dell'utente in ASP.NET, vedere l'articolo MSDN "User Input Validation in ASP.NET" all'indirizzo http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/pdc_userinput.asp (in inglese). |
| • | Per ulteriori informazioni sulla proprietà Secure dei cookie, vedere RFC2109 sul sito Web W3C all'indirizzo http://www.w3.org/Protocols/rfc2109/rfc2109 (in inglese). |
| • | Per ulteriori informazioni sulle considerazioni scaturite dal concorso Open Hack, vedere l'articolo di MSDN "Building and Configuring More Secure Web Sites" all'indirizzo http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/openhack.asp (in inglese). |