Both Windows and Passport authentication are seldom practical for real-world Internet applications. Windows authentication is based on Windows accounts and NTFS ACL tokens and, as such, assumes that clients are connecting from Windows-equipped machines. Useful and effective in intranet and possibly in some extranet scenarios, Windows authentication is simply unrealistic in more common situations because the Web application users are required to have Windows accounts in the application's domain. The same conclusion applies to Passport authentication, although for different reasons. Passport is not free, requires the implementation of serious security measures (that are not free and that you don't necessarily need at all sites), and makes sense mostly for e-commerce and co-branded Web sites.
So what is the ideal authentication mechanism for real Web developers? A Web programming best-practice recommends that you place some relatively boilerplated code on top of each nonpublic page and redirect the user to a login page. On the login page, the user is prompted for credentials and redirected to the originally requested page, if successfully authenticated. All this code is not exactly rocket science, but it's still code that you have to write yourself and use over and over again.
Forms authentication is just the ASP.NET built-in infrastructure to implement the aforementioned pattern for login. Forms authentication is the ideal (I would say the only) choice whenever you need to collect user credentials and process them internally for example, against a database of user accounts. 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.
You set up an ASP.NET application for Forms authentication by tweaking its root web.config file. You enter the following script:
<system.web> <authentication mode="Forms"> <forms loginUrl="login.aspx" /> </authentication> <authorization> <deny users="?" /> </authorization> </system.web>
The <authentication> section indicates the URL of the user-defined login form. ASP.NET displays the form only to users who have explicitly been denied access in the <authorization> section. The ? symbol indicates any anonymous, unauthenticated users. Note that the anonymous user here is not the IIS anonymous user but simply a user who has not been authenticated through your login form.
All blocked users are redirected to the login page, where they are asked to enter their credentials.
Note | The Forms authentication mechanism protects any ASP.NET resource located in a folder for which Forms authentication and authorization is enabled. Note that only resource types explicitly handled by ASP.NET are protected. The list includes .aspx, .asmx, and .ashx files, but not plain HTML pages or classic ASP pages. |
Form-based authentication is governed by an HTTP module implemented in the FormsAuthenticationModule class. The behavior of the component is driven by the contents of the web.config file. When the browser attempts to access a protected resource, the module kicks in and attempts to locate an authentication ticket for the caller. In ASP.NET 1.x, a ticket is merely a cookie with a particular (and configurable) name. In ASP.NET 2.0, it can be configured to be a value embedded in the URL (cookieless Forms authentication).
If no valid ticket is found, the module redirects the request to a login page. Information about the originating page is placed in the query string. The login page is then displayed. The programmer creates this page, which, at a minimum, contains text boxes for the user name and the password and a button for submitting credentials. The handler for the button-click event validates the credentials using an application-specific algorithm and data store. If the credentials are authenticated, the user code redirects the browser to the original URL. The original URL is attached to the query string of the request for the login page, as shown here:
http://YourApp/login.aspx?ReturnUrl=original.aspx
Authenticating a user means that an authentication ticket is issued and attached to the request. When the browser places its second request for the page, the HTTP module retrieves the authentication ticket and lets the request pass.
Let's see how form-based authentication works in practice and consider a scenario in which users are not allowed to connect anonymously to any pages in the application. The user types the URL of the page for example, welcome.aspx and goes. As a result, the HTTP module redirects to the login page any users for which an authentication ticket does not exist, as shown in Figure 15-3.
Figure 15-3: The login page of the sample application.
Important | There are inherent security concerns that arise with Forms authentication. Unfortunately, with today's browser technology these potential security concerns can be removed only by resorting to secure channels (HTTPS). I'll return to this topic later in the "General Security Issues" section. |
The layout of a login page is nearly the same a couple of text boxes for user name and password, a button to confirm, and perhaps a label to display error messages. However, you can make it as complex as needed and add as many graphics as appropriate. The user enters the credentials, typically in a case-sensitive way, and then clicks the button to log on. When the login page posts back, the following code runs:
void LogonUser(object sender, EventArgs e) { string user = userName.Text; string pswd = passWord.Text; // Custom authentication bool bAuthenticated = AuthenticateUser(user, pswd); if (bAuthenticated) FormsAuthentication.RedirectFromLoginPage(user, false); else errorMsg.Text = "Sorry, yours seems not to be a valid account."; }
The event handler retrieves the strings typed in the user name and password fields and calls into a local function named AuthenticateUser. The function verifies the supplied credentials and returns a Boolean value. If the user has been successfully authenticated, the code invokes the RedirectFromLoginPage static method on the FormsAuthentication class to inform the browser that it's time to issue a new request to the original page.
The RedirectFromLoginPage method redirects an authenticated user back to the originally requested URL. It has two overloads with the following prototypes:
public static void RedirectFromLoginPage(string, bool); public static void RedirectFromLoginPage(string, bool, string);
The first argument is the name of the user to store in the authentication ticket. The second argument is a Boolean value that denotes the duration of the cookie, if any, being created for the authentication ticket. If this argument is false, the cookie is given the normal duration that is, the number of minutes set by the timeout attribute (which is 30 minutes by default). If this argument is true, the cookie is given a much longer lifetime (50 years). Finally, the third argument optionally specifies the cookie path.
The authenticating algorithm that is, the code inside the AuthenticateUser method seen earlier is entirely up to you. For example, you might want to check the credentials against a database or any other user-defined storage device. The following listing shows a function that compares user name and password against the firstname and lastname columns of the Northwind Employees table in SQL Server 2000:
private bool AuthenticateUser(string username, string pswd) { // Performs authentication here string connString = "..."; string cmdText = "SELECT COUNT(*) FROM employees " + "WHERE firstname=@user AND lastname=@pswd"; int found = 0; using(SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd = new SqlCommand(cmdText, conn); cmd.Parameters.Add("@user", SqlDbType.VarChar, 10).Value = username; cmd.Parameters.Add("@pswd", SqlDbType.VarChar, 20).Value = pswd; conn.Open(); found = (int)cmd.ExecuteScalar(); conn.Close(); } return (found > 0); }
The query is configured to return an integer that represents the number of rows in the table that match the specified user name and password. Notice the use of typed and sized parameters in the SQL command as a line of defense against possible injection of malicious code. Notice also that the SQL code just shown does not support strong passwords because the = operator doesn't perform case-sensitive comparisons. To make provisions for that, you should rewrite the command as follows:
SELECT COUNT(*) FROM employees WHERE CAST(RTRIM(firstname) AS VarBinary)=CAST(RTRIM(@user) AS VarBinary) AND CAST(RTRIM(lastname) AS VarBinary)=CAST(RTRIM(@pswd) AS VarBinary)
The CAST operator converts the value into its binary representation, while the RTRIM operator removes trailing blanks.
Figure 15-4 shows the page of the application once the user has been successfully authenticated.
Figure 15-4: The user has been authenticated, and his name shows up in the user interface.
The welcome.aspx page has the following, fairly simple, source code:
<%@ Page Language="C#" CodeFile="Welcome.aspx.cs" Inherits="Welcome" %> <html><body> <form runat="server"> <h1>Welcome, <%=User.Identity.Name %></h1> </form> </body></html>
While an explicit sign-in is always required by Web sites that need authentication, an explicit sign-out is less common but legitimate nonetheless. The Forms authentication module provides an explicit method to sign out. The SignOut method on the FormsAuthentication class takes no argument and resets the authentication ticket. In particular, when cookies are used, the SignOut method removes the current ticket from the Cookies collection of the current HttpResponse object and replaces it with an empty and expired cookie.
After you call SignOut, you might want to redirect the application to another page for example, the home page. To do this, you don't have to redirect the browser, but as long as the page is publicly accessible, you can use the more efficient Server.Transfer method that we described in Chapter 12:
void Signout(object sender, EventArgs e) { FormsAuthentication.SignOut(); Server.Transfer("home.aspx"); }
In ASP.NET 2.0, the FormsAuthentication class has a new method RedirectToLoginPage that provides the described functionality, except that it uses Response.Redirect instead of Server.Transfer.
We've just covered the basics of Forms authentication, but we've not yet covered the programming API we find in ASP.NET 2.0 in any detail. Before we get to this, it is important that you take a look at the methods of the FormsAuthentication class and the configurable parameters you find in the web.config file. After this, I'll move on to introduce the membership API and role management.
The FormsAuthentication class supplies some static methods that you can use to manipulate authentication tickets and execute basic authentication operations. You typically use the RedirectFromLoginPage method to redirect an authenticated user back to the originally requested URL; likewise, you call SignOut to remove the authentication ticket for the current user. Other methods and properties are for manipulating and renewing the ticket and the associated cookie.
Table 15-4 lists the properties of the FormsAuthentication class. As you can see, many of them deal with cookie naming and usage and expose the content of configuration attributes in the <forms> section. We'll look at the underpinnings of the <forms> XML configuration element in the next section. The FormsAuthentication class features several new properties in ASP.NET 2.0. All the properties shown in the table are static.
Property | Description |
---|---|
CookieDomain | Returns the domain set for the authentication ticket. This property is equal to the value of the domain attribute in the <forms> section. Not supported in ASP.NET 1.x. |
CookieMode | Indicates whether Forms authentication is implemented with or without cookies. Not supported in ASP.NET 1.x. |
CookiesSupported | Returns true if the current request supports cookies. Not supported in ASP.NET 1.x. |
DefaultUrl | Returns the URL for the page to return after a request has been successfully authenticated. Matches the defaultUrl attribute in the <forms> section. Not supported in ASP.NET 1.x. |
EnableCrossAppRedirects | Indicates whether redirects can span different Web applications. Not supported in ASP.NET 1.x. |
FormsCookieName | Returns the configured cookie name used for the current application. The default name is .ASPXAUTH. |
FormsCookiePath | Returns the configured cookie path used for the current application. The default is the root path /. |
LoginUrl | Returns the configured or default URL for the login page. Matches the loginUrl attribute in the <forms> section. Not supported in ASP.NET 1.x. |
RequireSSL | Indicates whether a cookie must be transmitted using only HTTPS. |
SlidingExpiration | Indicates whether sliding expiration is enabled. |
Most of the properties are initialized with the values read from the <forms> configuration section of the web.config file when the application starts up.
Table 15-5 details the methods supported by the FormsAuthentication class. All the methods listed in the table are static.
Method | Description |
---|---|
Authenticate | Attempts to validate the supplied credentials against those contained in the configured <credentials> section. (I'll say more about this later.) |
Decrypt | Given a valid authentication ticket, it returns an instance of the FormsAuthenticationTicket class. |
Encrypt | Produces a string containing the printable representation of an authentication ticket. The string contains, encoded to URL-compliant characters, the user's credentials optionally hashed and encrypted. |
GetAuthCookie | Creates an authentication ticket for a given user name. |
GetRedirectUrl | Returns the redirect URL for the original request that caused the redirect to the login page. |
HashPasswordForStoringInConfigFile | Given a password and a string identifying the hash type, the method hashes the password for storage in the web.config file. |
Initialize | Initializes the FormsAuthentication class. |
RedirectFromLoginPage | Redirects an authenticated user back to the originally requested URL. |
RedirectToLoginPage | Performs a redirect to the configured or default login page. Not supported in ASP.NET 1.x. |
RenewTicketIfOld | Conditionally updates the sliding expiration on an authentication ticket. |
SetAuthCookie | Creates an authentication ticket and attaches it to the outgoing response. It does not redirect to the originally requested URL. |
SignOut | Removes the authentication ticket. |
The Initialize method is called only once in the application's lifetime and initializes the properties in Table 15-4 by reading the configuration file. The method also gets the cookie values and encryption keys to be used for the application.
Not available to ASP.NET 1.x applications, RedirectToLoginPage fills a hole in the programming interface of the FormsAuthentication class. The method is useful when a user signs out and you want to redirect her to the login page afterwards. When this happens, the method figures out what the login page is and calls Response.Redirect.
Note | In spite of their names, in ASP.NET 2.0 both the GetAuthCookie method and the SetAuthCookie method get and set an authentication ticket, whatever it means to the application. If the application is configured to do Forms authentication in a cookieless manner, the two methods read and write ticket information from and to the URL of the request. They read and write a cookie if the authentication method is configured to use cookies. |
Although ASP.NET Forms authentication is fairly simple to understand, it still provides a rich set of options to deal with to fine-tune the behavior of the authentication mechanism. Most of the settable options revolve around the use of cookies for storing the authentication ticket. All of them find their place in the <forms> section under the <authentication> section.
Forms authentication is driven by the contents of the <forms> section child of the <authentication> section. The overall syntax is shown here:
<forms name="cookie" loginUrl="url" protection="All|None|Encryption|Validation" timeout="30" requireSSL="true|false" slidingExpiration="true|false" path="/" enableCrossAppsRedirects="true|false" cookieless="UseCookies|UseUri|AutoDetect|UseDeviceProfile" defaultUrl="url" domain="string"> </forms>
The various attributes are described in Table 15-6.
Attribute | Description |
---|---|
cookieless | Defines if and how cookies are used for authentication tickets. Feasible values are UseCookies, UseUri, AutoDetect, and UseDeviceProfile. Not supported in ASP.NET 1.x. |
defaultUrl | Defines the default URL to redirect after authentication. The default is default.aspx. Not supported in ASP.NET 1.x. |
domain | Specifies a domain name to be set on outgoing authentication cookies. (I'll say more about this later.) Not supported in ASP.NET 1.x. |
enableCrossAppRedirects | Indicates whether users can be authenticated by external applications when authentication is cookieless. The setting is ignored if cookies are enabled. When cookies are enabled, cross-application authentication is always possible. (I'll cover more issues related to this as we go along.) Not supported in ASP.NET 1.x. |
loginUrl | Specifies the URL to which the request is redirected for login if no valid authentication cookie is found. |
name | Specifies the name of the HTTP cookie to use for authentication. The default name is .ASPXAUTH. |
path | Specifies the path for the authentication cookies issued by the application. The default value is a slash (/). Note that some browsers are case-sensitive and will not send cookies back if there is a path case mismatch. |
protection | Indicates how the application intends to protect the authentication cookie. Feasible values are All, Encryption, Validation, and None. The default is All. |
requireSSL | Indicates whether an SSL connection is required to transmit the authentication cookie. The default is false. If true, ASP.NET sets the Secure property on the authentication cookie object so that a compliant browser does not return the cookie unless the connection is using SSL. Not supported in ASP.NET 1.0. |
slidingExpiration | Indicates whether sliding expiration is enabled. The default is false, meaning that the cookie expires at a set interval from the time it was originally issued. The interval is determined by the timeout attribute. Not supported in ASP.NET 1.0. |
timeout | Specifies the amount of time, in minutes, after which the authentication cookie expires. The default value is 30. |
The defaultUrl attribute lets you set the default name of the page to return after a request has been successfully authenticated. This URL is hard-coded to default.aspx in ASP.NET 1.x and has been made configurable in ASP.NET 2.0. But isn't the URL of the return page embedded in the query string, in the ReturnUrl parameter? So when is defaultUrl useful?
If a user is redirected to the login page by the authentication module, the ReturnUrl variable is always correctly set and the value of defaultUrl is blissfully ignored. However, if your page contains a link to the login page, or if it needs to transfer programmatically to the login page (for example, after the current user has logged off), you are responsible for setting the ReturnUrl variable. If it is not set, the URL stored in the defaultUrl attribute will be used.
In ASP.NET 1.x, Forms authentication is exclusively based on cookies. The content of the authentication ticket is stored in a cookie named after the value of the name attribute in the <forms> section. The cookie contains any information that helps to identify the user making the request.
By default, a cookie used for authentication lasts 30 minutes and is protected using both data validation and encryption. Data validation ensures that the contents of the cookie have not been tampered with along the way. Encryption uses the Triple-DES (3DES) algorithm to scramble the content.
When validation is turned on, the cookie is created by concatenating a validation key with the cookie data, computing a message authentication code (MAC) and appending the MAC to the outgoing cookie. The validation key and the hash algorithm to use for the MAC are read out of the <machineKey> section in the web.config file. The same section also specifies the cryptographic key for when encryption is enabled.
Important | When you create a new ASP.NET application with Microsoft Visual Studio .NET, the default web.config file added to the application might not include most of the settings mentioned in this chapter and earlier in the book. It is interesting to note that both the web.config file in the application's root and any others you might have in subdirectories override the settings defined in the machine.config file. Installed with ASP.NET, machine.config contains a default value for each possible section and attribute in the configuration scheme. Therefore, if any given setting is not set (or overridden) in your web.config file, that configurable parameter will be set when you run your ASP.NET application using the default value found in machine.config. |
Using cookies requires some support from the client browser. In ASP.NET 1.x, cookies are mandatory if you want to take advantage of the built-in authentication framework. 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.
In ASP.NET 2.0, when cookieless authentication is on the ticket it is incorporated into the URL in much the same way as for cookieless sessions. Figure 15-5 provides an example.
Figure 15-5: Cookieless Forms authentication in action.
The URL of the page served to an authenticated user follows the pattern shown here:
http://YourApp/(F(XYZ 1234))/samples/default.aspx
The ticket, properly encoded to a URL-compliant alphabet, is inserted in the URL right after the server name.
Note | No matter which settings you might have for validation and encryption, or whether your authentication scheme is cookied or cookieless, the information stored in the authentication ticket is encoded so that it is not immediately human-readable. Forms authentication uses a URI-safe derivative of the Base64 encoding that carries six bits of encoding per character. |
Cookieless authentication requires an ISAPI filter to intercept the request, extract the ticket, and rewrite the correct path to the application. The filter also exposes the authentication ticket as another request header. The same aspnet_filter.dll component that we saw in Chapter 13 for cookieless sessions is used to parse the URL when cookieless authentication is used. To avoid confusion, each extra piece of information stuffed in the URL is wrapped by unique delimiters: S( ) for a session ID and F( ) for an authentication ticket. The filter extracts the information, removes URL adornments, and places the ticket information in a header named AspAuthenticationTicket.
The cookieless attribute in the <forms> section specifies if and how cookies are used to store the authentication ticket. The attribute can take any of the values listed in Table 15-7.
Value | Description |
---|---|
AutoDetect | Uses cookies if the browser has cookie support currently enabled. It uses the cookieless mechanism otherwise. |
UseCookie | Always uses cookies, regardless of the browser capabilities. |
UseDeviceProfile | Uses cookies if the browser supports them; uses the cookieless mechanism otherwise. When this option is used, no attempt is made to check whether cookie support is really enabled for the requesting device. This is the default option. |
UseUri | Never uses cookies, regardless of the browser capabilities. |
There's a subtle difference between UseDeviceProfile and AutoDetect. Let's make it clear with an example. Imagine a user making a request through Internet Explorer 6.0. The browser does have support for cookies, as reported in the browser capabilities database installed with ASP.NET. However, a particular user might have disabled cookies support for her own browser. AutoDetect can correctly handle the latter scenario and opts for cookieless authentication. UseDeviceProfile doesn't probe for cookies being enabled and stops at what's reported by the capabilities database. It will wrongly opt for cookied authentication, causing an exception to be thrown.
For compatibility with ASP.NET 1.x, the default value for the cookieless attribute is UseDevice-Profile. You should consider changing it to AutoDetect.
Let's tackle a few less obvious issues that might arise when working with Forms authentication.
HTTP cookies support a path attribute to let you define the application path the cookie is valid within. Pages outside of that path cannot read or use the cookie. If the path is not set explicitly, it defaults to the URL of the page creating the cookie. For authentication cookies, the path defaults to the root of the application so that it is valid for all pages in the application. So far so good.
Already in ASP.NET 1.x, two applications in the same Internet domain can share their own authentication cookies, implementing a sort of single sign-on model. Typically, both applications provide their own login pages and users can log on using any of them and then freely navigate between the pages of both. For this to happen, you only have to ensure that some settings in the root web.config files are the same for both applications. In particular, the settings for name, protection, and path attributes in the <forms> section must be identical. Moreover, 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" />
Read Knowledge Base article 312906 (located at http://support.microsoft.com/default.aspx?scid=kb;en-us;312906) for suggestions on how to create machine keys. Note that by default, validation and decryption keys are set to AutoGenerate. The keyword indicates that a random key has been generated at setup time and stored in the Local Security Authority (LSA). LSA is a Windows service that manages all the security on the local system. If you leave the AutoGenerate value, each machine will use distinct keys and no shared cookie can be read.
Applications running in the same domain can share authentication cookies also in ASP.NET 2.0. This happens also between an ASP.NET 1.x and ASP.NET 2.0 application as long as they are hosted in the same domain. Cookie sharing is impossible between ASP.NET 1.0 applications and both ASP.NET 1.1 and ASP.NET 2.0 applications.
Suppose now you run two ASP.NET Web sites named www.contoso.com and blogs.contoso.com. Each of these sites generates authentication cookies not usable by the other. This is because, by default, authentication cookies are associated with the originating domain. All HTTP cookies, though, support a domain attribute, which takes the flexibility of their path attribute to the next level. If set, the domain attribute indicates the domain for which the cookie is valid. Cookies can be assigned to an entire Internet domain, a subdomain, or even multiple subdomains.
In ASP.NET 2.0, the Domain attribute in the <forms> section determines the value of the domain attribute on the authentication cookie being created:
<forms domain="contoso.com" />
Add the preceding script to the web.config file of the Web sites named www.contoso.com and blogs.contoso.com and they'll share the authentication cookies (provided the client browser recognizes the domain attribute of the cookie, which most modern browsers are bound to do).
The effect of the setting is that the primary domain (www) and any other subdomains will be able to handle each other's authentication cookies, always with the proviso that their web.config files are synchronized on the machine key values.
Note | Setting the domain attribute doesn't cause anything to be emitted into the authentication ticket; it simply forces all Forms authentication methods to properly set the domain property on each issued or renewed ticket. The attribute is ignored if cookieless authentication is used. The domain attribute of the <forms> section takes precedence over the domain field used in the <httpCookies> section and is valid for all cookies created in the ASP.NET application. |
Forms authentication also supports having the login page specified in another application in the same Web site:
<forms loginUrl="/anotherAppsamples/login1.aspx" />
The two applications must have identical machine keys configured for this to work. If the application is using cookied authentication tickets, no additional work is necessary. The authentication ticket will be stored in a cookie and sent back to the original application. This is possible both in ASP.NET 1.x and ASP.NET 2.0.
In ASP.NET 2.0, if cookieless authentication is used, some extra work is required to enable the external application to authenticate for us. You need to 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. Figure 15-6 shows an example.
Figure 15-6: Another application performs the authentication and serializes the ticket back.
If the enableCrossAppRedirects attribute is missing and cookieless authentication is used, the external application will throw an exception.
A hacker who manages to steal a valid authentication ticket is in position to perpetrate a replay attack for the lifetime of the ticket which might be for as long as 50 years if you choose to create ASP.NET persistent tickets! To mitigate the risk of replay attacks, you can perform authentication over a secured socket.
This means that first you must deploy your login page on an HTTPS-capable server, and second you need to set the requireSSL attribute to true in the <forms> section. This setting causes the ASP.NET application to enable the Secure attribute on the HTTP cookie being created. When the Secure attribute is set, compliant browsers will send back only the cookie containing the ticket over a resource that is protected with SSL. In this way, you can still use a broad cookie scope, such as the whole application ('/' ), while providing a reasonable security level for the ticket in transit.
If you don't want to use SSL to protect the ticket, the best you can do to alleviate the risk of replay attacks is set the shortest lifetime for the authentication ticket to a value that is reasonable for the application. Even if the ticket is intercepted, there won't be much time remaining for the attacker to do his or her (bad) things.
Tip | What if you don't want to be bound to a fixed time for the ticket expiration, but still don't want to leave perpetual tickets around? You can make the application emit persistent tickets that last as long as 50 years, but change the validation and encryption keys in the web.config file when you think it's about time to renew all the tickets. A change in the web.config file will restart the application, and the new cryptographic parameters will make issued tickets invalid. |
As a final note regarding SSL, consider the following. If requireSSL is set and the user attempts to log in on a request not made over SSL, an exception is thrown, as shown in Figure 15-7. If requireSSL is set and an authentication cookie (a possibly stolen one at that) is provided over a non-SSL request, no exception is thrown but the cookie is wiped out and a regular login page is displayed through the browser.
Figure 15-7: An application configured to issue secure cookies can accept requests only over SSL.
Note that if the same happens with cookieless authentication, no protocol check is made, and the request is served to the user or to the attacker.
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, with Forms authentication credentials are sent out as clear text from the client. (See Figure 15-8.) SSL can be used to protect the communication, but in the end, Forms authentication is as weak as the IIS Basic authentication.
Figure 15-8: Credentials are sent out as clear text.
Note | The tool in the figure IEWatch 2.0 is a plug-in for Microsoft Internet Explorer that allows you to analyze HTTP/HTTPS requests and the HTML source code. You can get it at http://www.iewatch.com. |
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 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 newest membership API in ASP.NET 2.0.