La sicurezza di ASP.NET e IIS Raffaele Rialdi Microsoft C# MVP http://mvp.support.microsoft.com MVP Profile http://snipurl.com/f0cv Blog http://blogs/ugidotnet.org/raffaele malta@vevy.com Vevy Europe SpA
Asp.net .aspx, .asmx, .asax, .ascx, .soap, .rem, ... Agenda Spoofing / Tampering Spying Sql/script Injection D.O.S. Autenticazione ("chi sei?") Autorizzazione ("cosa posso fare?") IIS Anonymous Basic Windows Certificate NTFS Access Control List (ACL) Asp.net .aspx, .asmx, .asax, .ascx, .soap, .rem, ... UrlAuthorizationModule FileAuthorizationModule None Windows Forms Passport Web Application Imperative Declarative NTFS LDAP SQL
Meccanismi di Autenticazione IIS Anonima Basic Digest Certificate Windows Asp.net Passport Forms IIS mappa l'utente su IUSR_nomemacchina Asp.net lo vede come "" Utente non viene riconosciuto anche se è in Lan Usano lo store delle credenziali di Windows Richiedono una CAL per ogni utente La password non viaggia sulla rete Credenziali in chiaro (necessita SSL)
Differenze architetturali tra IIS5 e IIS6 Un account per App Pool. (token di processo) Devono essere membri del gruppo IIS_WPG Configurabile da IIS aspnet (default) Configurabile nel machine.config Network_service (default) User1 User2 App Pool 1 App Pool 2 App Pool 3 worker process (aspnet_wp) worker process (w3wp) worker process (w3wp) worker process (w3wp) appdomain appdomain appdomain appdomain appdomain appdomain appdomain appdomain appdomain Una web application per appdomain inetinfo (IIS) account: localsystem inetinfo (IIS) account: localsystem HTTP.SYS (Kernel Mode)
Windows Authentication step-by-step Web.config di default è pronto: Impostare le autorizzazioni Disabilitare l'autenticazione anonima in IIS ... (prossime slide) L'utente autenticato è: (stringa vuota se anonimo) L'utente usato dal worker process è: <authentication mode="Windows" /> <authorization> <deny users="?" /> <allow users="*" /> </authorization> ? utente anonimo * tutti gli utenti HttpContext.Current.User.Identity.Name System.Security.Principal.WindowsIdentity.GetCurrent().Name
Internet Information Server IIS5 (Windows 2000 / XP Pro)
Internet Information Server IIS6 (Windows 2003)
IIS6 Application pool
Impersonation (solo con Windows Authentication) Il token di security dell'utente autenticato viene impostato sul thread. Il token di processo rimane invariato. Se l'utente è anonimo, viene impersonato IUSR_NomePc Sintassi (web.config): <identity impersonate=true /> IIS5 non può eseguire più worker process sotto identità diverse Soluzione: impersonation di un utente specifico <identity impersonate=true user="xxx" password="zzz" />
I problemi architetturali di Impersonation Molti vogliono usare la security di Sql server Se il db è in rete, impersonation non funziona ma ci vuole invece delegation Si perde il controllo centralizzato della security (accedere a Ntfs, Ldap, risorse in rete, DB) La security 'per righe' fatta con sql server è un incubo
I problemi tecnologici di Impersonation Il token dell'utente non può essere usato per accedere a risorse remote (per es. la webapp non può usarlo per accedere un db in rete) La soluzione viene con Delegation che è di default disabilitata (proprio perchè è pericolosa!) Impersonation implica contesti diversi per ciascun utente. Questo significa niente connection pooling Protezione limitata. Un eventuale buffer overrun può usare sia il token di thread (impersonato) che quello di processo (worker process) usando RevertToSelf. Se chiamo un componente COM che sta in un apartment diverso, COM non userà il token di impersonazione ma quello di processo
Scenario tipico di una webapp worker process token di processo = aspnet token di thread (solo se usa impersonation) Browser Firewall Web Server Data Server Alice aspnet Anche abilitando impersonation il Data Server verrà accesso come aspnet
Forms Authentication step-by-step Abilitare l'autenticazione anonima in IIS Impostare l'autenticazione e i suoi parametri Impostare le autorizzazioni Creare la pagina di login controllare l'utente e autorizzarlo <authentication mode="Forms"> <forms name="myCookieName" loginUrl="~/Login.aspx" /> </authentication> <authorization> <deny users="?" /> <allow users="*" /> </authorization> ? utente anonimo * tutti gli utenti if(UserDB.Check(txtUsername.Text, txtPassword.Text)) { FormsAuthentication.RedirectFromLoginPage( txtUsername.Text, ckRemember.Checked); }
Forms Authentication gestire i ruoli Gestire l'evento Application_AuthenticateRequest Impostare le autorizzazioni per singole parti del sito protected void Application_AuthenticateRequest(Object sender, EventArgs e) { UserDB.AssignRoles(); } <location path="Backoffice.aspx"> <system.web> <authorization> <deny users="?" /> <allow roles="admins" /> <deny users="*" /> </authorization> </system.web> </location> L'ordine di valutazione delle autorizzazioni è dal primo verso l'ultimo. Il primo 'match' vince.
Forms Authentication Gestione utenti e ruoli Si costruisce una piccola classe: public class UserDB { public static bool CheckUser(string Username, string Password) return (Username == Password);// Solo per la demo!!! ;-) } public static void AssignRoles() IPrincipal CurrentUser = HttpContext.Current.User; if(CurrentUser != null && CurrentUser.Identity.IsAuthenticated && CurrentUser.Identity.AuthenticationType == "Forms") string User = CurrentUser.Identity.Name; string [] roles = GetRolesForUser(User); CurrentUser = new System.Security.Principal.GenericPrincipal (CurrentUser.Identity, roles); private static string[] GetRolesForUser(string User) string[] roles = new string[2]; roles[0] = "Administrators"; roles[1] = "Users"; return roles; // Solo per la demo!
Principal e Identity La sicurezza basata sui ruoli secondo il framework IIdentity rappresenta l'identità di un utente WindowsIdentity, FormsIdentity, PassportIdentity, GenericIdentity IPrincipal contiene l'Identity e i ruoli WindowsPrincipal, GenericPrincipal AuthenticationType String. "Windows", "Forms", "Passport", ... Bool. Indica se l'utente è autenticato String. Nome dell'utente IsAuthenticated Name Identity IIdentity. Bool. Indica se l'utente appartiene ad un certo ruolo (gruppo) IsInRole
Forms Authentication Gestione utenti Gli utenti si possono anche gestire nel web.config ma è sconsigliato: <authentication mode="Forms"> <forms name="myCookieName" loginUrl="~/Login.aspx"> <credentials passwordFormat = "SHA1" <user name="UserName1" password="SHA1EncryptedPassword1"/> </credentials> </forms> </authentication>
Esempio Forms Authentication
Forms Authentication Tip Diciamo di avere due Web Application ... Prendiamo in considerazione: Nome del cookie della Forms authentication Path del cookie Il tag <machineKey ... /> nel web.config (vedi http://support.microsoft.com?id=312906) Se sono identici, l'utente potrà navigare da una all'altra senza doversi ri-autenticare Se almeno uno di questi è diverso, sarà necessario ri-autenticarsi
Autenticazione mista Windows / Forms Il problema: In Windows Authentication, l'header HTTP "LOGON_USER" contiene il nome utente Se IIS è configurato come anonimo, NON viene passato il nome utente anche se siamo loggati sul dominio ... Ma la Forms authentication richiede che IIS sia configurato come anonimo (altrimenti compare la dialog di autenticazione)
Autenticazione mista Windows / Forms La soluzione: Due pagine di Login: Forms e Windows Web.config configurato per la Forms Autorizzazione a tutti per la pagina di Login Windows IIS – WebApp: abilitare accesso anonimo IIS – LoginWin.aspx: togliere accesso anonimo LoginWin.aspx: Crea il ticket della Forms authenticaion a partire dalle credenziali Windows <location path="LoginWin.aspx"> <system.web> <authorization> <allow users="*" /> </authorization> </system.web> </location>
Esempio autenticazione mista
Forms Authentication con LDAP LDAP è un protocollo per dialogare con Active Directory Posso chiedere con LDAP: di verificare le credenziali di un utente su AD di darmi l'elenco dei gruppi a cui appartiene quell'utente Il codice per fare queste due cose è qui: http://support.microsoft.com/?id=326340 Metodo 1: Public Function IsAuthenticated(ByVal domain As String, ByVal username As String, ByVal pwd As String) As Boolean Metodo 2: Public Function GetGroups() As String Un ottimo motivo per usarla è nelle WebApp con autenticazione mista Windows + Forms
Asp.net <authorization ... /> Asp.net PrincipalPermission, etc. Dove siamo? Autenticazione IIS Basic, Win, ... Asp.net Passport, Form Raffaele Autorizzazione IIS NTFS Asp.net <authorization ... /> Pagina si/no Controllo Imperativo Controllo dichiarativo Asp.net PrincipalPermission, etc. Codice si/no
Sicurezza imperativa e dichiarativa Gli attrezzi del mestiere: IPrincipal.IsInRole() Imperativa (bool) PrincipalPermission.Demand() Imperativa (SecurityException) PrincipalPermissionAttribute Dichiarativa (SecurityException) if(User.IsInRole("Admins")) { ... } PrincipalPermission perm = new PrincipalPermission(null, "Admins"); perm.Demand(); PrincipalPermissionAttribute [PrincipalPermission(SecurityAction.Demand, Role="Admins")] public void MyAdminMethod() {...}
Esempio SecureHandler Role Based Authorization
Mai dare informazioni preziose default: <customErrors mode="RemoteOnly" /> Qualsiasi informazione sugli errori può essere sfruttata da un hacker. Gli errori custom (che nascondono i dettagliati): mode="Off" mostrati a nessuno mode="On" mostrati a tutti mode="RemoteOnly" solo in remoto Questo meccanismo è poco elastico Possiamo usare un HttpModule per migliorare la situazione ....
CustomErrorHandler (esempio) Web.Config: <customErrors mode="On" defaultRedirect="~/HttpErrors.aspx" /> .... <httpModules> <add type="CustomErrorHandler.RafErrorModule, CustomErrorHandler" name="RafErrorModule"/> </httpModules> Due pagine di gestione errore: SoftError.aspx (per utenti) e HardError.aspx (per admin) Il Module redirige gli errori a seconda del ruolo dell'utente Il Module gestisce gli errori Http e le Exception ... vediamo il codice ...
Esempio CustomErrorHandler
Se però voglio accettare una stringa html/script dall'utente? HttpRequestValidationException <%@ Page validateRequest="true" %> (true by default) Riconosce un eventuale input malizioso dell'utente e lancia l'eccezione HttpRequestValidationException Se però voglio accettare una stringa html/script dall'utente? Opzione 1: validateRequest = false (vale per tutta la pagina) e validarla. Opzione 2: criptare sul client, decrittarla sul server e validarla. Sul client (durante la onsubmit) si cripta il contenuto con encode di javascript il contenuto criptato si mette dentro un <input type=hidden> sul server si usa HttpUtility.UrlDecode per decodificare la stringa La validazione ... si può usare Server.HtmlEncode per farlo apparire sulla pagina si può fare il parsing per eliminare i tag pericolosi
<Input type=hidden /> Spesso viene usato un campo hidden per conservare i dati tra un postback e l'altro La modifica (tampering) dei campi hidden è banale e, se non controllata adeguatamente, può comportare un duro attacco. Soluzione: Criptarli prima di mandarli al client Decrittarli dentro un try/catch quando tornano al server
Protezione del Viewstate Il Viewstate di default è un campo <input type=hidden /> Il Viewstate contiene lo stato dei controlli sul lato server Se non è criptato, è facilmente visibile: http://www.pluralsight.com/toolcontent/ViewStateDecoder11.zip Soluzioni: Criptarlo: <%@ Page enableViewStateMac=“true” /> Mac = machine authentication check La chiave e il metodo di encryption sono specificati nel tag <machineKey> del machine.config (autogenerazione) Per crearne e specificarne di nuovi nel web.config: Q312906 Salvarlo sul server: http://www.aspalliance.com/articleViewer.aspx?aId=72&vId=&pId= http://msdn.microsoft.com/msdnmag/issues/03/02/CuttingEdge/default.aspx
Proteggere le risorse Molti file non devono poter essere scaricati via http dall'utente Nel machine.config Asp.net protegge di default alcuni tipi di file dal download (.cs, .config, ...) Soluzione 1: custodirlo fuori dalla cartella virtuale Soluzione 2: proteggere il file via NTFS (se si usa impersonation.) Soluzione 3: proteggere con asp.net Associare i file da proteggere in IIS all'Isapi di Asp.net Proteggere (ad esempio) i file mdb nel web.config: <httpHandlers> <add verb="*" path="*.mdb" type="System.Web.HttpForbiddenHandler" /> </httpHandlers>
CAS e ASP.net Livello di Trust Restrizioni della CAS Full High Medium Nessuna restrizione sulle permission. Applicazioni possono accedere a risorse controllate dalle impostazioni del sistema operativo Possono essere fatte operazioni che richiedono un alto privilegio High Non si può chiamare codice nativo Non si possono usare serviced components Non si può scrivere nell'event log Non si può accedere alle code di msmq Non si può usare data source di tipo OLE DB Medium L'accesso ai file è ristretto alla cartella dell'applicazione Non si può accedere al registry Low Non si può accedere a SQL Server Non si può chiamare CodeAccessPermission.Assert Minimal Si ha solo il diritto di esecuzione e nulla di più
Q & A ....
© 2003-2004 Microsoft Corporation. All rights reserved. This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.
SQL Injection Un pirata può devastare il db ... string strSql = "Select * from authors where au_lname like '" + TextBox1.Text + "'"; SqlCommand cmd = new SqlCommand(strSql, Cnn); SqlDataReader dr = cmd.ExecuteReader(); Select * from authors where au_lname like ' ' ; drop authors - - ' Prima query Seconda query Scartato
SQL Injection ... usare i parameters!!! ... I Parameters incrementano anche le performance: non c'è conversione da string a tipo sul db la query rimane compilata e preparata sul db server string strSql = "Select * from authors where au_lname like @au_lname"; SqlCommand cmd = new SqlCommand(strSql, Cnn); cmd.Parameters.Add("@au_lname", SqlDbType.VarChar,40); SqlDataReader dr = cmd.ExecuteReader(); exec sp_executesql N'Select * from authors where au_lname like @au_lname', N'@au_lname varchar(40)', @au_lname = ' ' ' ; drop authors - - ' apice raddoppiato da ADO.NET Gli apici non sono l'unico problema: select * from titles where royalty = 0 ; drop authors
XSS: Cross Site Scripting From: Hacker To: Raffaele Subject: Free gift Click here to win Normale navigazione Email con link contenente un attacco XSS Il link effetua una GET sul sito della banca con la QueryString La banca (non protetta da XSS) restituisce nella pagina html lo script inviato Lo script viene eseguito dal browser e le informazioni riservate arrivano al pirata
Mappare una estensione in IIS slide post-sessione Affinchè asp.net (e quindi handlers e moduli) abbiano il controllo di un tipo di file (.jpg nell'esempio) è necessario configurare IIS Path dell'isapi di asp.net (copiarla da quella di .aspx)
Promemoria slide post-sessione Installate XP SP2 e Win2K3 SP1! Aggiornare sempre con Windows Update Proteggere i dati sensibili DPAPI per criptare (vedi Pattern & Practices sul sito MSDN) ASPnet_setreg.exe per salvare credenziali criptate nel registry (per esempio di sql). Vedi Q329290 per i dettagli. Eseguire logging e auditing Usare UrlScan. UrlScan Monitor (Lorenzo Barbieri) http://www.gotdotnet.com/Community/Workspaces/workspace.aspx?id=c859a9fd-3cfd-4d2d-bbe9-4bcc334ed2c3 IIS LockDown (solo IIS5) Secure Configuration Wizard (solo IIS6 W2K3 SP1)