ASP Security
ASP.Net Security
- IIS authentication:
- Anonymous access= IUSER_machinename (ie same as ASP).
- Windows login- Basic (clear text), Digest (encrypted) and Windows (NTLM)
- IIS passes to ISAPI filter- eg aspnet_wp.exe (or w3wp.exe on WServer2003).
- If system.web/identity/@impersonate='false' (default)
- No impersonation= run as ASPNET (or see machine.config's processModel)
- Impersonation= run as IIS user (IUSER_machine or windows login) or system.web/identity/@userName
- Use system.web/authentication/@mode (None, Windows, Forms, Passport).
- If using Windows, set system.web/identity/@impersonate="true" otherwise it won't work.
- If using forms
- set system.web/authorization/deny/@users="?" otherwise it won't work
- system.web/authentication/forms/@loginUrl
- @timeout- default is 30mins, but it refreshes halfway- so 16mins inactivity may kill it...
- @protection="All" (encrypted + validated) - in webfarms, set machine.config machineKey/@validationKey + @decryptionKey
- In code
if (FormsAuthentication.Authenticate(user, pass)) FormsAuthentication.RedirectFromLoginPage(user, isPermaCookie);
(replace first method with database authentication)
(if loginpage== default.aspx then redirect may be to login page... try .GetRedirectUrl instead)
(asp.net 2.0 has new Membership class)
- Page.User returns an IPrincipal, with IsInRole and Identity (which is an IIdentity, with AuthenticationType, Name- cast as WindowsIdentity for IsAnonymous etc).
Rights needed for ASPNET process (KB)
| IIS | web.config mode | WindowsIdentity | Page.User |
|---|---|---|---|
| Anon | forms | no impersonate=IUSR_MACHINE impersonate= ASPNET |
formId |
| Anon | windows | "" | |
| Windows | forms | no impersonate=ASPNET impersonate= domain/user |
formId |
| Windows | windows | domain/user |
NB: HttpContext.Current.User != WindowsIdentity.GetCurrent() (unless windows impersonating)
!= COM+ Security Role (ContextUtil.IsCallerInRole() is a different set of roles!
- Page.User
- Empty string if IIS= anonymous + web.config = windows authentication.
- Otherwise form ID (forms auth) or domain/user (win auth)
- WindowsIdentity.GetCurrent()
- IUSR_MACHINE if impersonate + IIS anonymous only
- ASPNET or NETWORK SERVICES if anon/impersonate=true OR windows/impersonate=false
- domain/user if IIS basic/digest/integrated and impersonate=true
Forms Authentication
In web.config
<authentication mode="Forms">
<forms loginUrl="Login.aspx"/>
</authentication>
<authorization>
<deny users="?" />
</authorization>
In your login page :
protected void btnLogin_Click(object sender, EventArgs e)
{
string userName = txtUserName.Text.Trim();
string password = txtPassword.Text.Trim();
//if (Membership.ValidateUser(userName, password)) //membership provider
if (ValidateUser(userName, password)) //local validation
{
//if using redirection
FormsAuthentication.RedirectFromLoginPage(userName, false);
//otherwise just set cookie
//FormsAuthentication.SetAuthCookie(userName, false);
}
else
{
Page.Validators.Add(new BusinessValidationError("Invalid UserID and Password"));
}
}
To implement a custom MembershipProvider see msdn sample (and here for web.config to set the membership defaultProvider)
To logout manually (you can also just use the asp:LoginStatus control):
FormsAuthentication.SignOut();
Response.Redirect(FormsAuthentication.GetLoginPage(null));
//if you did NOT specify forms/@cookieless=UseCookies (or are using cookieless) you are automatically redirected
Roles
To do role management (User.IsInRole(x), authorization/allow/@roles, sitemap roles) you have to manually create a FormsAuthenticationTicket and put it in a cookie (internally this is what FormsAuthentication.SetAuthCookie does). NB: you have about 1k of the 4k cookie size maximum for the role list- otherwise use Cache (you can't use Session, it isn't available early enough).
string userRoles = "Admin, PowerUser"; //from database?
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // version
userName, // user name
DateTime.Now, // issue time
DateTime.Now.AddMinutes(30),// expires
false, // persistent
userRoles // user data
);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
cookie.Secure = FormsAuthentication.RequireSSL;
cookie.Domain = FormsAuthentication.CookieDomain;
cookie.HttpOnly = true; //a little extra security
Response.Cookies.Add(cookie);
Response.Redirect("Secure.aspx");
Now wire this in the global.asax (or use an HttpModule)
void Application_OnAuthenticateRequest(object sender, EventArgs e)
{
HttpContext c = HttpContext.Current;
if (c.Request.IsAuthenticated)
{
FormsIdentity id = (FormsIdentity)c.User.Identity;
string[] roles = id.Ticket.UserData.Split(',');
System.Security.Principal.GenericPrincipal p =
new System.Security.Principal.GenericPrincipal(c.User.Identity, roles);
Context.User = System.Threading.Thread.CurrentPrincipal = p;
}
}
Simple Authentication
For quick and simple/dirty security, you can put users directly into the web.config.
Nested web.config: You must only set <authentication> on the top level. You can't set it on subfolder web.config or in <location>. You should instead use different <authorization> sections.
<authentication mode="Forms">
<forms loginUrl="~/Admin/Login.aspx">
<credentials passwordFormat="Clear">
<user name="Martin" password="secret"/>
</credentials>
</forms>
</authentication>
Then just use the login control.
<asp:Login ID="Login1" runat="server" DisplayRememberMe="False" VisibleWhenLoggedIn="False"
OnAuthenticate="Login1_Authenticate" />
<script runat="server">
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
e.Authenticated = FormsAuthentication.Authenticate(Login1.UserName, Login1.Password);
}
</script>
Saving new users into web.config. If you're doing this, you really should be using a database, but hey ho. Also you really should use SHA1 format.
if (!Page.IsValid) return;
string userName = Server.HtmlEncode(txtName.Text);
string pw = Server.HtmlEncode(txtPassword.Text);
Configuration config = WebConfigurationManager.OpenWebConfiguration("~/");
//also (AuthenticationSection)WebConfigurationManager.GetWebApplicationSection("system.web/authentication") but users is readonly;
AuthenticationSection auth = (AuthenticationSection)config.SectionGroups["system.web"].Sections["authentication"];
string passwordFormat = auth.Forms.Credentials.PasswordFormat.ToString();
if (passwordFormat != "Clear")
pw = FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, passwordFormat);
auth.Forms.Credentials.Users.Add(new FormsAuthenticationUser(userName, pw));
try
{
config.Save();
Response.Redirect(Request.CurrentExecutionFilePath);// Redirect to self.
}
catch (ConfigurationErrorsException ex)
{
Label1.Text = "ASP.NET process account (ASPNET or Network Service) must have write permission granted for the Web.config file<br/>";
Label1.Text += "Manually copy this xml into your web.config forms/credentials section:<br/>";
Label1.Text += string.Format("<user name=\"{0}\" password=\"{1}\" />", userName, pw);
}