"Non esiste una patch per la stupidità"
http://www.sqlsecurity.com
ASP.NET Security
Raffaele Rialdi
MVP.NET
[email protected]
http://mvp.support.microsoft.com
MVP Profile: http://snipurl.com/7vyf
Agenda
Spoofing / Tampering
Spying
Sql/script Injection
D.O.S.
Autenticazione
("chi sei?")
•
•
•
•
Anonymous
Basic
Windows
Certificate
•
•
•
•
None
Windows
Forms
Passport
Autorizzazione
("cosa posso fare?")
IIS
• NTFS Access Control List
(ACL)
Asp.net
.aspx, .asmx, .asax, .ascx, .soap, .rem, ...
Web Application
NTFS
LDAP
• UrlAuthorizationModule
• FileAuthorizationModule
• Imperative
• Declarative
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)

Windows Authentication
step-by-step
1.
Web.config di default è pronto:
<authentication mode="Windows" />
2.
Impostare le autorizzazioni
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
3.
?  utente anonimo
*  tutti gli utenti
Disabilitare l'autenticazione anonima in IIS ...
(prossime slide)
4.
L'utente autenticato è:
(stringa vuota se anonimo)
HttpContext.Current.User.Identity.Name
5.
L'utente usato dal worker process è:
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 a livello di thread viene sostituito con quello
dell'utente autenticato.
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
Forms Authentication
step-by-step
1.
2.
Abilitare l'autenticazione anonima in IIS
Impostare l'autenticazione e i suoi parametri
<authentication mode="Forms">
<forms name="myCookieName" loginUrl="~/Login.aspx" />
</authentication>
3.
Impostare le autorizzazioni
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
4.
?  utente anonimo
*  tutti gli utenti
Creare la pagina di login controllare l'utente e autorizzarlo
if(UserDB.Check(txtUsername.Text, txtPassword.Text))
{
FormsAuthentication.RedirectFromLoginPage(
txtUsername.Text, ckRemember.Checked);
}
Forms Authentication
gestire i ruoli
1.
Gestire l'evento Application_AuthenticateRequest
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
UserDB.AssignRoles();
}
2.
Impostare le autorizzazioni per singole parti del sito
<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
AuthenticationType

String. "Windows", "Forms", "Passport", ...
IsAuthenticated

Bool. Indica se l'utente è autenticato

String. Nome dell'utente
Name

IPrincipal contiene l'Identity e i ruoli
WindowsPrincipal, GenericPrincipal
Identity

IIdentity.
IsInRole

Bool. Indica se l'utente appartiene ad un certo ruolo (gruppo)
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:
1.
Nome del cookie della Forms authentication
2.
Path del cookie
3.
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
Dove siamo?
Autenticazione
IIS
Basic, Win, ...
Autorizzazione
IIS
NTFS
Controllo
Imperativo
Controllo
dichiarativo
Asp.net
Passport, Form
Asp.net
<authorization ... />
Asp.net
PrincipalPermission, etc.
Raffaele
Pagina
si/no
Codice
si/no
Sicurezza
imperativa e dichiarativa

Gli attrezzi del mestiere:

IPrincipal.IsInRole()
 Imperativa (bool)
if(User.IsInRole("Admins")) { ... }

PrincipalPermission.Demand()
 Imperativa (SecurityException)
PrincipalPermission perm = new PrincipalPermission(null, "Admins");
perm.Demand();

PrincipalPermissionAttribute
 Dichiarativa (SecurityException)
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)
1.
Web.Config:
<customErrors mode="On" defaultRedirect="~/HttpErrors.aspx" />
....
<httpModules>
<add type="CustomErrorHandler.RafErrorModule,
CustomErrorHandler" name="RafErrorModule"/>
</httpModules>
2.
Due pagine di gestione errore:
SoftError.aspx (per utenti) e HardError.aspx (per admin)
3.
Il Module redirige gli errori a seconda del ruolo dell'utente
4.
Il Module gestisce gli errori Http e le Exception
... vediamo il codice ...
Esempio
CustomErrorHandler
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 ' '
Prima query
;
drop authors
-- '
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




1. Normale navigazione
2. Email con link contenente un attacco XSS
3. Il link effetua una GET sul sito della banca con la
QueryString
4. La banca (non protetta da XSS) restituisce nella pagina
html lo script inviato
5. Lo script viene eseguito dal browser e le informazioni
riservate arrivano al pirata
Contromisura di asp.net 1.1
<%@ 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.
1.
2.
3.
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:
1.
Criptarlo: <%@ Page enableViewStateMac=“true” />
Mac = machine authentication check


2.
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>
Mappare una estensione in IIS

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)
Q&A
....
Scarica

ASP.NET Security