CoreCoder
LANGUAGES: C# | VB.NET
ASP.NET VERSIONS: 2.0
User Authentication in ASP.NET 2.0
What s New in the Way of Authenticating Users in ASP.NET 2.0 Internet Applications
By Dino Esposito
Experts and gurus agree that the most secure application is the application that survives and resists actual attacks, not the application designed to prevent and stop possible attacks. A concrete example of this empiric rule is that you need to choose the proper authentication scheme based on the characteristics of the application. The same group of experts and gurus also agree that security must be devised and implemented as a native feature of the application. You can t simply take an existing application, overpay a renowned super consultant, and ask him or her to make your application, all of a sudden, secure.
Unfortunately, it just doesn t work this way. Security can t be bolted on an existing application, and it can t be something that makes your application difficult and bothersome to use and manage. Let s focus on ASP.NET applications. Aside from infrastructural security aspects (account security and folder permissions), user authentication and authorization is the primary aspect of ASP.NET application security. ASP.NET provides three authentication schemes: Passport, Windows, and Forms. The rule I apply in my projects is quite simple and unequivocal: a) do not use Passport; b) use Windows if the ASP.NET application targets an intranet; c) use Forms if the ASP.NET application is exposed over the Internet.
In this article, I ll explain what s good and bad with these three options, as well as what s new and interesting in ASP.NET 2.0 in this regard.
Why Passport Authentication?
As of today, Passport authentication is difficult to implement and requires the implementation of additional serious security measures (which are not free and which you don t necessarily need at all sites); it makes sense mostly for e-commerce and co-branded Web sites. On the other hand, Passport provides a way to authenticate users coming across all the sites that participate in the initiative. Users need to do a single logon and, if successfully authenticated, they can then freely move through all the member sites. In addition to the single logon service, Passport also offers core profile services for member sites.
As a matter of fact, and regardless of any technical considerations, the Passport centralized authentication service has not proven to be a very well received proposal in the industry. In my opinion, this is enough explanation for now.
However, the architectural niche that Passport fills should become more common in the future. The idea behind Passport authentication is not far-fetched and was quite good indeed. For example, the more Service Oriented Applications (SOA) are being talked about and designed, the more need for a centralized authentication mechanism arises. It could be that in a few months, Passport revamps under the covers of Microsoft Identity Integration Server (MIIS). For more information, visit http://www.miis-alliance.com.
Why Windows Authentication?
When configured to use Windows authentication, ASP.NET accepts the security token determined by IIS and doesn t perform any further tasks. The real authentication is performed by IIS according to one of its built-in authentication methods: Basic, Digest, or Integrated Windows. To be successfully authenticated, the user must have an account set up on the Web server machine. In most cases, Windows authentication is used in conjunction with file authorization. To ensure that only authorized users can access specific pages, you define an access control list (ACL) on the file that includes references to the authorized accounts.
Windows authentication is seldom practical in an application that is exposed to the Internet. Requiring that each registered user has an account on the Web server may not be convenient or even doable. In general, Windows authentication is appropriate whenever you have an application open to a relatively small community of users for which authentication is required. The larger your audience, the more you should consider alternate forms of authentication.
Why Forms Authentication?
When you determine that Windows authentication doesn t work for you, and you re looking for an alternate form of authentication, like in a pun, the solution has the name of Forms authentication. Part of the ASP.NET infrastructure, Forms authentication surrounds each served page with a standard block of code aimed at finding a valid ticket attached to the request. If no ticket is found, the user is redirected to a login page and prompted for credentials. If any entered credentials are reckoned valid, the authentication code attaches a ticket to the request and reiterates the request.
The login pattern implemented by Forms authentication doesn t look radically different from Windows and Passport authentication. The key difference is that, with Forms authentication, everything happens under the strict control of the application. In addition, Forms authentication is a non-obtrusive solution that doesn t require any modification on the Web server environment.
Cookieless Authentication in ASP.NET 2.0
By default, the authentication ticket is saved in an HTTP cookie, but using cookies requires some support from the browser. In ASP.NET 1.x, cookies are the only possible way to implement form-based authentication. In ASP.NET 2.0, the core API also supports cookieless semantics. More precisely, the whole API has been reworked to make it expose a nearly identical programming interface, but also support dual semantics: cookied and cookieless.
When cookieless authentication is on, the contents of the ticket are packed into the URL. Here s an example:
http://YourApp/(F(XYZ...1234))/samples/default.aspx
The information stored in the authentication ticket is encoded not to be human-readable. The string inserted in the URL is a Base64 encoding of the bits that result from the encryption. To turn on cookieless authentication, set the cookieless attribute in the <forms> section of the web.config file:
<forms name="cookie"
loginUrl="url"
protection="All|None|Encryption|Validation"
timeout="30"
requireSSL="true|false"
slidingExpiration="true|false"
enableCrossAppsRedirects="true|false"
cookieless="UseCookies|UseUri|AutoDetect|UseDeviceProfile" />
</forms>
The feasible values for the cookieless attribute are listed and described in Figure 1. ASP.NET 1.x uses cookies no matter the capabilities of the browser. A browser might not be able to receive cookies for two reasons: the browser doesn t recognize cookies or the user disabled cookie support for the browser on the local machine. In ASP.NET 2.0, when UseDeviceProfile is used, if the browser is known to support cookies, authentication is implemented via cookies even if the user disabled cookies. If this is the case, an error is reported. Much smarter is the AutoDetect option. AutoDetect can correctly detect if the browser is effectively in the condition of receiving and handling cookies. If this is not the case, AutoDetect opts for cookieless authentication. Note that to not break compatibility with ASP.NET 1.x, the default value for the cookieless attribute is UseDeviceProfile. You should seriously consider changing it to AutoDetect.
Value |
Description |
AutoDetect |
Uses cookieless schema if the browser currently doesn t accept cookies. Uses cookies if the browser can accept them. |
UseCookie |
Uses cookies, no matter the browser s capabilities. |
UseDeviceProfile |
Uses cookies if the browser supports cookies, regardless of the fact that cookies can actually be disabled. This is the default option. |
UseUri |
Never uses cookies, no matter the browser capabilities. |
Figure 1: Feasible values for the cookieless attribute.
Sharing Authentication Cookies
The mechanics of Forms authentication doesn t change significantly in ASP.NET 2.0, so I assume I can take it for granted and move on and tackle a couple of more advanced issues. The first is, how two sites can share ASP.NET authentication tickets.
In ASP.NET 1.x, two applications in the same Internet domain can share their own authentication cookies by implementing a sort of single sign-on model. You only ensure that settings in the web.config file of both applications match as far as authentication and machine key are concerned. In particular, a <machineKey> section should be added to both web.config files with explicit validation and decryption keys:
<machineKey
validationKey="C50B3C89CB21F4F1422FF158A5B42D0...E"
decryptionKey="8A9BE8FD67AF6979E7D20198C...D"
validation="SHA1" />
By default, validation and decryption keys are set to AutoGenerate, meaning that a random key has been generated at set-up time, then stored. If you leave the AutoGenerate value, each machine will use distinct keys and no shared cookie could be read across machines. Applications running in the same Internet domain can share authentication cookies also in ASP.NET 2.0. The same also works if ASP.NET 1.x and ASP.NET 2.0 applications are involved as long as they are hosted in the same Internet domain.
But what if you are running two Web sites, like www.mysite.com and mail.mysite.com? Because authentication cookies are associated with the originating domain, these two sites emit incompatible cookies. However, it should be noted that HTTP cookies support a domain attribute to set the Internet domain for which the cookie is valid. Furthermore, cookies can be assigned to an entire Internet domain, a sub-domain, or even multiple subdomains. Here s the trick. In ASP.NET 2.0, the new domain attribute in the <forms> section determines the value of the domain attribute on any authentication cookie being created:
<forms domain="mysite.com" />
By adding the following markup in the web.config files of both sites, you put them in the condition of sharing their authentication cookies. This doesn t work if cookieless authentication is used.
Enabling Cross-application Authentication
Especially when multiple sites and sub-sites are coordinated together, it might be desirable to use a distinct application to authenticate users. This is a non-issue. You simply specify the correct URL and go. Here s a quick example:
<forms loginUrl= /CentralAuth/login.aspx />
The two applications must have identical machine keys configured. If cookieless authentication is used, some extra work is required to enable the external application to authenticate. You must set the enableCrossAppRedirects attribute in <forms> in the web.config file of both applications:
<forms enableCrossAppRedirects="true" />
Upon successful authentication, the ticket is generated and attached to a query string parameter to be marshaled back to the original application. If the enableCrossAppRedirects attribute is missing, and cookieless authentication is used, the external application will throw an exception. If cookied authentication is used, this approach works without further work.
But what if the authentication engine lives in another site or another machine? In this case, specifying a fully qualified loginUrl attribute doesn t work. Imagine the following being used on http://desktop:
<forms loginUrl="http://laptop/CentralAuth/login.aspx" />
The remote login page is correctly displayed and the user is correctly authenticated. By design, though, the return URL appended to request for the login page lacks the server information. In this way, the remote authentication service has no memory of the server that made the call. To work around this issue, you must tweak the code of the login page. Typically, after the authentication succeeds, you call the RedirectFromLoginPage method from the FormsAuthentication class. This method blindly redirects to the URL specified in the ReturnUrl query string parameter. You need to replace this method with the code in Figure 2. The code works nearly like the original RedirectFromLoginPage method, except that it adds host information to the redirect URL. With this trick you can successfully use any external application to authenticate (either version 1.x or 2.0) and deploy just about anywhere.
void RedirectFromLoginPage(string userName, _
bool createPersistentCookie, string cookiePath)
{
FormsAuthentication.Initialize();
if (userName != null)
{
FormsAuthentication.SetAuthCookie(userName, _
createPersistentCookie, cookiePath);
string url = HttpContext.Current.Request["ReturnUrl"];
url = FixUrl(url);
HttpContext.Current.Response.Redirect(url, false);
}
}
string FixUrl(string url)
{
// Add server information to the URL
}
Sub RedirectFromLoginPage(ByVal userName As String, _
ByVal createPersistentCookie As Boolean, _
ByVal cookiePath As String) _
FormsAuthentication.Initialize();
If Not userName Is Nothing Then
FormsAuthentication.SetAuthCookie(userName, _
createPersistentCookie, cookiePath)
Dim url As String = _
HttpContext.Current.Request("ReturnUrl")
url = FixUrl(url)
HttpContext.Current.Response.Redirect(url, false)
End If
End Sub
Function FixUrl(ByVal url As String) As String
' Add server information to the URL
End Function
Figure 2: A replacement for RedirectFromLoginPage.
The Bottom Line
Functionally speaking, Forms authentication is the most appropriate authentication method for Web and ASP.NET applications. However, a few general security issues can t pass unnoticed. To start with, be aware that with Forms authentication, credentials are sent out as clear text from the client. SSL should be used to protect the communication, but in the end, Forms authentication is as weak as the IIS Basic authentication.
A stolen authentication cookie can be used to plan replay attacks as long as the cookie remains valid. This risk can be partially mitigated by reducing the lifetime of the authentication cookie. In this regard, avoid creating persistent cookies ASP.NET will make them last as long as 50 years.
Last but not least, Forms authentication is based on application code, which is good and bad news at the same time. It s good because you can keep everything under control; it s bad because your own bugs could open a security hole.
Dino Esposito is a Solid Quality Learning mentor and author of Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005). Based in Italy, Dino is a frequent speaker at industry events worldwide. Join the blog at http://weblogs.asp.net/despos.