There is a standard set of web-based authentication methods that may reasonably be expected by application developers on a particular platform. Generally supported authentication mechanisms include HTTP authentication (basic and digest), forms/cookies authentication, and certificate authentication. The latter is usually coupled with mutual SSL/TLS authentication -- this is the standard way of implementing it.
Forms authentication is normally performed with the help of a cookies mechanism. Two types of cookies are in use: temporary and persistent. The former last only during the current browser session, and the latter are stored at the client's computer. Both types have an expiration time, to prevent identity theft, but persistent cookies are typically stored on the client's computer and remain valid for many days, so they pose a greater security threat.
Certificate authentication is significantly more secure, as it allows mutual authentication, so the client can be assured that it connects to the proper server. Configuring it, however, is more problematic, because it requires installing an X509 certificate on the client side.
ASP.NET heavily relies on IIS for its authentication needs. In fact, it uses ISS to implement all of the above authentication modes, except for forms, and merely consumes the results of the authentication process performed by IIS. Therefore, forms authentication is the only one that is going to be further discussed here for ASP.NET.
Authentication is handled by
FormsAuthenticationModule, which handles all traffic received from IIS via the extensions mechanism, as shown in Figure 1. It passes all authenticated requests through, while forwarding all unauthenticated ones to the specified logon page. The authentication, once performed, is sustained via the cookie mechanism, which can be made to expire after a timeout to prevent user identity theft. Alternatively, another form-authentication scheme can be developed; for example, using hidden fields to store credentials in the form, and taking full control of authentication process by providing a
FormsAuthentication_OnAuthenticate handler event in the Global.asax file. It is possible (although not trivial) to create a completely cookieless authentication schema using this method.
Figure 1. ASP.NET Forms Authentication
ASP.NET provides a helper class,
FormsAuthentication, to help with common authentication tasks: authenticating username and password; issuing, encrypting, and decrypting tickets; redirecting a user request to the originally requested page after successful authentication; and signing out. An authenticated user is identified by the presence of an authentication cookie (either temporary or persistent), which is usually implemented by the
FormsAuthenticationTicket class. However, a custom cookie may be set in the code -- this allows better control over its expiration property, as well as over the cookie's content.
string data = "Application data"; HttpCookie cookie = new HttpCookie( FormsAuthentication.FormsCookieName, data); //expires in 10 minutes cookie.Expires = DateTime.Now + new TimeSpan(0,0,10,0); Response.Cookies.Add(cookie);
Custom content in a cookie may be protected by encrypting, via the
FormsAuthentication.Encrypt call, with a user-provided or auto-generated 3DES key, which is then stored at the server's Local Security Authority (LSA). HMAC validation with a specified algorithm may also be requested.
<machineKey validationKey="AutoGenerate" decryptionKey="AutoGenerate" validation="SHA1"/>
The associated authentication cookie's name and expiration timeout may be configured in the top-level application's web.config file. Cookies are renewed upon the next request after half of their expiration time has passed, thus keeping them from expiring. However, as was explained earlier, the server-side session associated with the user's identity expires after a certain period of inactivity, as determined by the
timeout setting in the
sessionState element. So, even if a request has a valid cookie, if the corresponding session has expired, the user will still be prompted to re-authenticate. Calling
FormsAuthentication.SignOut will terminate any session association of the current user and remove cookies from the browser's cache.
<authentication mode="Forms"> <forms forms="DemoApp" Loginurl="https://www.DemoApp.com/login.aspx" Name=".DEMOAPPCOOKIE" protection="All" Timeout="30" Path="/"> </forms> </authentication>
The authentication sequence works in the following way: after a request comes in, it is forwarded to the
OnAuthenticate handler, if present. Here, any additional information may be extracted from the custom cookie or URL, and additional roles assigned. If a user identity is associated with the request after the finishing handler's execution, no further actions are taken. Otherwise, the request is checked by name for the presence of authentication cookie. If such a cookie is found, it is used to construct the appropriate
Principal and associate it with the current request; otherwise, the request is forwarded to the logon page.
The Java servlet specification requires support for the basic HTTP authorization mechanism, and encourages (but does not require!) support for digest authentication, because it is a fairly rare mechanism. Additionally, two other forms of authentication are required for J2EE compatibility: form-based and mutual certificate (HTTPS client). Basic, digest, and certificate authentication are carried out transparently, between the web server and the connecting client, and do not require writing additional code.
The form-based authentication schema is probably the most common option in use today. The specifications require the presence of the following names on the logon form: a
j_security_check action, and the fields
j_password. These indicate to the servlet engine that this is the logon information to process.
<form method="POST" action="j_security_check"> <input type="text" name="j_username"> <input type="password" name="j_password"> </form>
A container creates a persistent or temporary cookie named
JSESSIONID for the user request, sets its expiration policy via a call to
Cookie.setMaxAge before adding the cookie to the current session, and then keeps sending it back to the client with each response. The client returns it with each request, which allows mapping the connectionless requests to the user's session. Alternatively, a container may use a technique called URL Rewriting to support those clients that do not accept cookies:
Note that using a
GET operation in form-based login is a bad idea -- it puts the entire request, with all of its fields, into the URL, thus making it available when browsing the server log. For instance:
POST operation, the URL will be as shown below, so the user information will not show up in the server's log:
When a request comes for one of the protected resources, the engine checks the user's authentication, and if he is has not been authenticated yet, forwards him to the login page associated with the resource. The servlet engine is then responsible for redirecting the user back to the originally requested resource (or error page, in case of a failure).
<web-app> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-error-page>/Error.html </form-error-page> <form-login-page>/SignOn.html </form-login-page> </form-login-config> </login-config> </web-app>
Certificate authentication is configured declaratively on the server side, but support for mutual HTTPS communication is required on both sides. The client's request should contain a certificate that can be mapped to a server's defined principal, which is going to be associated with this and further requests. Note that HTTPS, as opposed to HTTP, is a stateful protocol, and cookies are not needed to maintain session association.
<web-app> <login-config> <auth-method>CLIENT-CERT</auth-method> </login-config> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </web-app>
From inside of a servlet, client certificate information can be retrieved by accessing the
getAttribute method of the
javax.servlet.http.HttpServletRequest class, requesting the following attribute:
X509Certificate cert = (X509Certificate)request.getAttribute ("javax.servlet.request.X509Certificate");
In the case of a HTTPS connection, the servlet engine sets this attribute, as required by the servlet specifications, before invoking the target servlet. Using attributes of the returned
X509Certificate object, the servlet can perform any additional programmatic authentication of the caller.
Note: .NET delegates all types of user authentication (except for forms) to IIS, and barely consumes the results. J2EE requires support for all standard authentication mechanisms from the compliant servers.