Programming Microsoft ASP.NET 4 - Dino Esposito [420]
Note that if the same happens with cookieless authentication, no protocol check is made and the request is served to the user…or the attacker.
General Security Issues
Functionally speaking, Forms authentication is the most appropriate authentication method for Web and ASP.NET applications. However, a few general security issues shouldn’t pass without comment.
To start with, Forms authentication credentials are sent out as clear text from the client. SSL can be used to protect the communication, but in the end Forms authentication is as weak as IIS Basic authentication.
As mentioned, a stolen authentication cookie can be used to plan replay attacks as long as it is valid. This risk can be partially mitigated by reducing the lifetime of the ticket. Requiring an SSL connection for the cookie transmission resolves the issue if cookied authentication is used, but not if a cookieless solution is employed.
Finally, Forms authentication is based on application code, which is good news and bad news at the same time. It is good because you can keep everything under control. It is bad because any bug you leave in your code opens a security hole. A way to mitigate the risk of vulnerabilities stemming from incorrect code is to resort to the membership API.
Creating a Custom Principal
The User property on the HttpContext object is of type IPrincipal—the public contract that all principal objects must fulfill. Most of the time, the real type behind the User property is GenericPrincipal. If role management is enabled at the application level, instead, the type is RolePrincipal. (We’ll cover role management in just a few moments.)
Common principal classes are certainly useful but may prove to be quite generic in most applications. In real-world scenarios, you sometimes need to add some custom information to the security context so that once you have authenticated a user you know much more about him than just the user name and roles. Let’s see how to tweak the authentication process to create a custom cookie and then how to retrieve that information and pack it into a custom principal object.
In the event handler responsible for validating credentials, you add the following code:
var customInfo = "some|value";
var ticket = new FormsAuthenticationTicket(
4, // Version number
userName, // Username
DateTime.Now, // Issue date
DateTime.Now.AddMinutes(30), // Expiration date
createPersistentCookie, // Is it persistent?
customInfo // User data
);
var encTicket = FormsAuthentication.Encrypt(ticket);
// Store the ticket into a cookie
var cookie = FormsAuthentication.GetAuthCookie(
FormsAuthentication.FormsCookieName,
createPersistentCookie);
cookie.Value = encTicket;
// Append the cookie to the response and redirect
HttpContext.Current.Response.Cookies.Add(cookie);
HttpContext.Response.Redirect(FormsAuthentication.DefaultUrl);
You create your own ticket and stuff some custom data in it. You must get your own instance of the FormsAuthenticationTicket class in order to do so. Next, you encrypt the ticket and write it to a cookie with the default name of authentication cookies. The preceding code replaces the following call, which is what would happen by default:
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
The next step is retrieving the custom information stored in the authentication cookie. You can do that in the authentication step of global.asax, as shown here:
protected void Application_PostAuthenticateRequest()
{
// Collect current security information
var principal = HttpContext.Current.User as GenericPrincipal;
if (principal == null)
return;
var identity = principal.Identity as FormsIdentity;
if (identity == null)
return;
// Extract user data in the authentication ticket