Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Asp Net 2.0 Security Membership And Role Management

.pdf
Скачиваний:
48
Добавлен:
17.08.2013
Размер:
12.33 Mб
Скачать

Chapter 5

Quick Recap on Forms Authentication

In Chapter 2, the sections on AuthenticateRequest, AuthorizeRequest and EndRequest described how forms authentication works throughout the HTTP pipeline. In summary, forms authentication performs the following tasks:

1.During AuthenticateRequest, the FormsAuthenticationModule checks the validity of the forms authentication ticket (carried in a cookie or in a cookieless format on the URL) if one exists. If a valid ticket is found, this results in a GenericPrincipal referencing a FormsIdentity as the value for HttpContext.Current.User. The actual information in the ticket is available as an instance of a FormsAuthenticationTicket off of the FormsIdentity.

2.During AuthorizeRequest, other modules and logic such as the UrlAuthorizationModule attempt to authorize access to the currently requested URL. If an authenticated user was not created earlier by the FormAuthenticationModule, any URL that requires some type of authenticated user will fail authorization. However, even if forms authentication created a user, authorization rules that require roles can still fail unless you have written custom logic to associate a FormsIdentity with a set of roles or used a feature like Role Manager that performs this association automatically.

3.If authorization fails during AuthorizeRequest, the current request is short-circuited and immediately forwarded to the EndRequest phase of the pipeline. The

FormsAuthenticationModule runs during EndRequest and if it detects that

Response.StatusCode is set to 401, the module automatically redirects the current request to the login page that is configured for forms authentication (login.aspx by default).

This basic summary of forms authentication demonstrates that the forms authentication ticket is the piece of persistent authentication information around which the forms authentication feature revolves. The next few sections delve into more details about how the forms authentication ticket is protected, persisted and passed around applications. For all practical purposes, developers use the terms “forms authentication ticket” and “forms authentication cookie” interchangeably.

Understanding Persistent Tickets

Since ASP.NET 1.0, the forms authentication feature has supported persistent and nonpersistent tickets. In ASP.NET 1.0 and 1.1 the forms authentication ticket was always stored in a cookie (again excluding the Mobile Internet Toolkit which most developers probably have not used). So, the decision between using a persistent versus nonpersistent ticket is a choice between using persistent or session-based cookies. The lifetime of a session-based cookie is the duration of the interactive browser session; when you shut down the browser, any session based cookies that were held in memory are gone. The forms authentication feature included the option for persistent cookies to enable lower-security applications (message boards, personal websites with minimal security requirements, and so on) to store a representation of the authenticated user without constantly requiring users to log in again.

Clearly for some sites where users infrequently access the application (and hence are always forgetting their credentials), persistent cookies are a great usability enhancement. The one “small” problem is that on ASP.NET 1.0 and ASP.NET 1.1 sites, persistent cookies are given a 50-year lifetime. Now I am all for making certain types of websites easier to use (like everybody else I have an idiotic number of user- name-password combinations to deal with), but I think 50 years is pushing it a bit! You can see this for

192

Forms Authentication

older ASP.NET sites that issue cookies if you take a look at the expiration date for their forms authentication tickets. For example the following code issues a persistent ticket:

FormsAuthentication.RedirectFromLoginPage(“testuser”, true);

The resulting expiration date on the cookie when I was writing this was “5/9/2055 7:52PM.” The net result is that a digitally encrypted and digitally signed forms authentication ticket is left lying around a user’s computer until by happenstance the cookie is deleted. On one hand, if you regularly delete cookies, then 50-year lifetimes are probably not a big deal. On the other hand, as a website developer you definitely can bet that some percentage of your user population is accruing cookies ad infinitum. From a security perspective the 50-year lifetime is really, really bad. Although the default security for forms authentication cookies encrypts and signs the cookies, it is likely that sometime in the next 50 years computing power will have reached a point that the present-day forms authentication ticket can be cracked in a reasonably short time. It’s unlikely that anybody will ever have their original computer from 50 years ago (where would you put that old UNIVAC today?). But some website users will still be on the same machine 5 to 7 years later, and if they regularly visit the same site, the forms authentication ticket issued years earlier will still be lying around waiting to be hijacked and cracked.

As a result of this type of security concern with excessively long-lived forms authentication tickets, in ASP.NET 2.0 persistent cookies now set their expiration based upon the value of the cookie timeout set in configuration. Taking the same code shown earlier, and running it on ASP.NET 2.0 with the default cookie timeout of 30 minutes, results in a persistent cookie that expires 30 minutes later (you can see this if you view the files in your browser cache and look for the cookie file). This change may take a number of developers by surprise, and their first inkling of the new behavior may be complaints from website users suddenly being forced to login.

However, even though the ASP.NET 2.0 behavior changes the cookie expiration for new cookies issued using forms authentication, the new behavior has no effect on preexisting cookies. If you upgrade an ASP.NET 1.1 application to ASP.NET 2.0, any users with 50-year cookies floating around will continue to retain these cookies. Even if you use sliding expiration for your forms authentication tickets, because ASP.NET hasn’t been around for 25 years, none of the preexisting persistent cookies will be reissued due to time passing for sliding expirations (forms authentication attempts to reissue a cookie when 50% or more of the configured cookie timeout has elapsed).

This raises the question of whether developers should take explicit steps to reissue their persistent cookies with more reasonable timeouts. I agree that a little more security is better than 50-year cookie lifetimes and recommend that developers using persistent forms authentication cookies add some logic to their applications after upgrade. First, developers should determine a reasonable persistent cookie timeout. This may be a few weeks or months, although I wouldn’t recommend going beyond one year. Even for sites that don’t care too much about security, it doesn’t seem unreasonable to ask people to reauthenticate themselves once a year.

ASP.NET 2.0 has only one cookie timeout setting (the timeout attribute in the <forms /> configuration element). If your site needs to issue a mixture of persistent and session-based cookies, both types of cookies will use the timeout set in configuration; however, expiration enforcement happens through different mechanisms. In these situations it makes sense to ask why a website (or perhaps a set of websites) mixes the comparatively insecure persistent cookie option with session-based forms authentication tickets. Websites that are cookie-based should use one type of cookie persistence for all website users, and stick with a single persistence model.

193

Chapter 5

After you have determined a new value for timeout, the next step is to add some code to your site that automatically swaps out the old persistent cookie for a new one with an updated expiration. PostAuthenticateRequest is a convenient point to perform this work. The following code for global.asax shows how this can be accomplished.

void Application_PostAuthenticateRequest(Object sender, EventArgs e)

{

if (User.Identity is FormsIdentity)

{

if (((FormsIdentity)User.Identity).Ticket.Expiration > (DateTime.Now.Add(new TimeSpan(0,40320,0))))

{

FormsAuthentication.RedirectFromLoginPage(User.Identity.Name, true);

}

}

}

The code first checks to see whether an authenticated FormsIdentity exists on the current context. If one exists, the IIdentity that is available from the User property on the context is cast to a FormsIdentity so that you can get access to the FormsAuthenticationTicket available off of the Ticket property. The FormsAuthenticationTicket conveniently exposes its expiration with the Expiration property. In the sample code, if the ticket expires more than 40320 minutes (roughly one month) from now, the credentials are reissued as a persistent ticket.

Running this code on ASP.NET 2.0 results in a forms authentication cookie being reissued but with the updated behavior for computing cookie expiration based on the timeout attribute in configuration. One thing to note is that the forms authentication API does not expose the value of the timeout attribute in a convenient manner. Although you could technically use the new strongly typed configuration classes in ASP.NET 2.0 to get the correct value, you can’t really depend on that approach if you plan to run in partial trust (more on issues with strongly typed configuration classes and partial trust in Chapter 4). As a result, the somewhat simplistic workaround is to duplicate the expiration value either by hard-coding it as in the sample code or, for better maintenance, by storing it as a value in a place like the <appSettings /> section in configuration.

How Forms Authentication Enforces Expiration

The timeout attribute on the <forms > configuration element controls the expiration of the forms authentication ticket. However, in the case of session based cookies the Expires property of the cookie created by forms authentication is never set. Furthermore, with the introduction of cookieless support in ASP.NET 2.0, there may not even be a cookie created for the forms authentication ticket.

Forms authentication computes the expiration time for a forms authentication ticket by adding the value of the timeout attribute to DateTime.Now. This value is passed as one of the parameters to the

FormsAuthenticationTicket constructor. After a FormsAuthenticationTicket is created, it is converted to a hex-string representation using some custom internal serialization logic. This means the expiration date is packaged within the custom serialized representation of the ticket, regardless of whether the ticket is subsequently issued as a cookie or is instead placed on the URL for the cookieless case.

Each time a forms authentication ticket arrives back at the web server, FormsAuthenticationModule opens either the cookie or the cookieless value on the URL, and converts the enclosed hex-string to an instance of FormsAuthenticationTicket. With a fully inflated ticket, the module checks the

194

Forms Authentication

Expiration property to determine whether the ticket is still valid. This means that when a ticket is carried inside a cookie, FormsAuthenticationModule ignores any implied statement about expiration. Technically, if a cookie is sent to the web server, the browser agent that sent the cookie must consider the cookie to still be valid, meaning that the cookie has not expired yet.

However, from a security perspective, it is trivial for a malicious user to generate a cookie and send it to the web server. As a result, forms authentication never depends on the expiration mechanism supported by HTTP cookies. It always consults the expiration date contained within the serialized ticket when determining whether the ticket is valid. If a cookie arrives at the web server, but the expiration date contained within the serialized ticket indicates that the ticket has expired, FormsAuthenticationModule recognizes this and doesn’t create a FormsIdentity based on the ticket. Furthermore, it removes the expired cookie from the Request.Cookies collection to prevent any downstream logic from making incorrect decisions based on the presence of the expired ticket.

This approach also has the side benefit of forms authentication performing date comparisons based on the web server’s time. Although clock-skew probably exists between the current time on the web server and the current time on a client’s machine, as long as the cookie gets sent to the web server, the expiration date comparison is made using the server’s time.

One question that arises from time to time is whether the expiration date of the ticket is maintained in Universal Coordinate Time (UTC). Unfortunately, when forms authentication was first implemented, it used the local date-time representation for the expiration date. In ASP.NET 2.0, the team considered changing this behavior through a configuration setting, but ultimately decided against it due to the following problems:

Changing to a UTC-based expiration would break authentication in mixed ASP.NET 1.1 and ASP.NET 2.0 environments. The ASP.NET 1.1 servers would think the expiration date was in local time, when in reality the time was offset by many hours from the local time (assuming that your web server wasn’t sitting in the GMT time zone of course!).

Although a configuration switch for ASP.NET 2.0 was a possibility, this would introduce a fair amount of confusion around when to turn it on or off. If the UTC time handling was turned on, and then later an ASP.NET 1.1 application was introduced into your web farm, ASP.NET 2.0 would have to be switched back to the original behavior.

In two scenarios, local times potentially introduce problems for computing expiration times.

In the United States, twice during the year clocks are reset forward or backward by one hour. When a forms authentication ticket that was issued before the clock reset is sent back to the web server, the forms authentication feature incorrectly interprets the local time in that ticket. This means that one of two things happens: an extra one hour is added to the ticket’s expiration, or one hour is subtracted from the ticket’s expiration. However, because this occurs at 1 AM local time (this for the United States time adjustments), there probably is not a lot of traffic on your website that will encounter this oddity.

If a website user browses across servers located in different physical time zones, and if the servers in each time zone are not set to use the same time zone internally, servers will incorrectly interpret the expiration date. For example, if a website load balances some of its users across servers on the West Coast and the East Coast of the United States, there is a three-hour time difference between the two coasts. If a forms authentication ticket is initially issued on the West coast at 10 AM local time, when the ticket is sent to a server on the East Coast, that server is going to compare the 10AM issuance against the fact that it is now 1 PM in the afternoon. This kind of discrepancy can lead to a user being forced to log in again.

195

Chapter 5

Because of these potential discrepancies developers should be aware of the limitations of the local date time value stored in the forms authentication ticket. In the case of the clocks being reset twice a year, the current behavior is likely limited to only a few night owls.

However, if your websites use geographic load balancing, keep in mind the forms authentication behavior. You could ensure that when a user has accessed a server in one geographic region, the user is routed back to the same geographic region on all subsequent requests. Alternatively, you could have a standard time zone that all servers use regardless of the time zone for the physical region that the servers are deployed in. On the other hand, if all of your geographically dispersed servers lie in the same time zone (maybe you have servers in New York City and others in Miami), you won’t run into the forms authentication expiration issue.

Working with the DateTime Issue with Clock Resets

You don’t need to read this section unless you are really, really curious about what happens when the server clock is reset! After struggling with this problem during the ASP.NET 2.0 design cycle, I figured I would share the code snippets and results.

The following code is for a simple console application that simulates the problem with date time comparisons when the clock resets.

static void Main(string[] args)

{

DateTime dtNow = DateTime.Now;

Console.WriteLine(“Use a 30 minute timeout just like forms authentication.”);

Console.WriteLine(“The date value for now is: “ + dtNow.ToShortTimeString());

Console.WriteLine(“Has the time expired: “ + (dtNow.Add(new TimeSpan(0, 30, 0)) < DateTime.Now));

string breakHere = “Manually reset the clock “;

DateTime dtNow2 = DateTime.Now;

Console.WriteLine(“The date value for now after the clock reset is: “ + dtNow2.ToShortTimeString());

Console.WriteLine(“Has the time expired: “ + (dtNow.Add(new TimeSpan(0, 30, 0)) < DateTime.Now));

Console.ReadLine();

}

Running this inside of the debugger with a breakpoint in the dummy string assignment in the middle allows you to set the clock forward or backward prior to the next date comparison. The comparison against DateTime.Now is the same the comparison that FormsAuthenticationTicket makes when you check the Expired property. Running the sample code, and setting the clock back one hour during the breakpoint results in the following output:

Use a 30 minute timeout just like forms authentication.

The date value for now is: 10:27 AM

Has the time expired: False

The date value for now after the clock reset is: 9:27 AM

Has the time expired: False

196

Forms Authentication

The net result is that after the clock was set back one hour (just as is done during the last Sunday of October in most of the United States), an expiration time based on a 30-minute timeout will be valid until 10:57 AM. However, with the clock reset back to 9:27 AM, the lifetime of a ticket with this expiration is accidentally extended to 90 minutes.

Running the same code, but this time setting the clock forwards one hour results in the following output:

Use a 30 minute timeout just like forms authentication.

The date value for now is: 10:33 AM

Has the time expired: False

The date value for now after the clock reset is: 11:33 AM

Has the time expired: True

Now the original expiration of 11:03 AM (10:33 AM issuance plus a 30-minute lifetime) is considered expired after the clock was set forward one hour (just as is done during the first Sunday in April). This occurs because after the clock is reset, the original expiration time of 11:03 AM (which is considered a local time) is compared against the newly updated local time of 11:33 AM and is considered to have immediately expired.

The underlying technical reason for this similar behavior with forms authentication tickets is twofold:

The serialization of the forms authentication ticket’s DateTime expiration uses a local time conversion (DateTime.ToFileTime and DateTime.FromFileTime). As a result, whenever a forms authentication ticket is deserialized on a web server, the .NET Framework hands back a DateTime instance that contains a local time value.

The Expired property on FormsAuthenticationTicket is always compared against DateTime.Now. For the ticket to have been UTC capable, you really need the ticket to be compared against DateTime.UtcNow.

There isn’t an easy workaround to this whole issue. Aside from physical deployment steps, you can take to prevent part of the problem, the only ironclad way to ensure handling for all of these scenarios is for you to take over much of the management and verification of the forms authentication ticket, including the following:

Manually construct the ticket and storing the UTC expiration date inside of the UserData property of the FormsAuthenticationTicket.

Manually issue the ticket.

Hook a pipeline event prior to AuthenticateRequest (for example, BeginRequest), or hook the Authenticate event on the FormsAuthenticationModule directly. Then manually crack open and verify the ticket based on the UTC date previously stored in the UserData property of the FormsAuthenticationTicket. If you detect a discrepancy between the UTC-based comparison, and the value of FormsAuthenticationTicket.Expired, you could force a redirect to reissue an updated cookie that contained an adjusted local time for the Expiration property.

Whether this effort is worth it depends on the specific kind of application you are trying to secure. I suspect that for all but the most sensitive sites (for example, financial sites), the extra effort to deal with time mismatches that occur twice a year will probably not warrant the investment in time and effort.

197

Chapter 5

Securing the Ticket on the Wire

By default, the forms authentication ticket is digitally encrypted and signed using a keyed hash. This security has been available since ASP.NET 1.0, and ASP.NET 2.0 uses the same security for the ticket. However, there have been some new questions over hash security and support for new encryption options in ASP.NET 2.0.

How Secure Are Signed Tickets?

Since ASP.NET 1.0, forms authentication tickets have been digitally signed using a keyed hash that uses the SHA1 algorithm. When SHA1 was originally chosen years ago, it was considered a very secure hashing algorithm with no likelihood of being cryptographically weakened. In 2005, there were reports that SHA1 had been “broken” — in the cryptographic community someone reported a theoretical collision-based attack on SHA1 hashes.

In summary, some researchers proposed a way to reduce the chance of inducing a hash collision in SHA1 to only 2^69 attempts. Normally, you would expect to take around 2^80 attempts to create a collision in SHA1 (SHA1 hashes are 160 bits in length, so you can figure that on average you only need to flip half as many possible bits to eventually find a piece of text that results in a matching SHA1 hash).

So, this new attack against SHA1 theoretically reduces the number of attempts by a pretty hefty 1208335523804270469054464 iterations (after notepad, I think calc.exe is the most frequently entered command from the Run option in Windows). Suffice it say that that the current estimate of 2^69 attempts to find a SHA1 collision would still entail enormous computing resources. Depending on who you believe, it takes a few million years with commodity hardware or a few years with specialized cracking computers backed by the resources of the NSA. Regardless, it all boils down to the fact that “breaking” SHA1 is still incredibly difficult and time-consuming and realistically isn’t feasible with 2005-class hardware.

However, in the cryptography community, weaknesses with hashing or encryption algorithms are like snowballs rolling down a steep hill. Weaknesses start out small, but as time passes and attacks are better understood, the combination of increased mathematical focus on these algorithms combined with ever increasing computing power eventually leads to present-day algorithms being susceptible to viable attacks.

Given the news about the SHA1 attack, there has been concern in the cryptography community around the long-term viability of SHA1 as a hashing algorithm. Some companies will probably start moving to SHA256 as a preemptive measure. There had been discussion on the ASP.NET team about whether one of the stronger SHA variants should have been added to <machineKey /> (remember that <machineKey /> defines the encryption and signing options for forms authentication among other things). However, the team decided to stick with SHA1 because technically speaking, forms authentication really uses HMACSHA1 (frequently referred to as a “keyed hash”), not just plain SHA1. In the case of <machineKey />, and thus forms authentication tickets, sticking with HMACSHA1 is a reasonable choice for the current ASP.NET 2.0 product.

The transient nature of nonpersistent forms authentication tickets means that in future framework releases, support for stronger SHA variants like SHA256 and SHA512 can be easily added. Such a change would impact applications that persistently store forms authentication tickets. Any application that truly needs security though should not be using persistent forms authentication tickets. The most likely future impact for developers would be around edge cases dependent on the total length of the

198

Forms Authentication

characters in a forms authentication cookie. The stronger SHA variants contain more bits, and thus require more hex characters when converted to a string representation. This is normally more of a concern for cookieless tickets where ticket lengths are constrained. I cover issues with cookieless forms authentication tickets, including effective length restrictions, later in this chapter.

Another reason for sticking with SHA1 as the hashing algorithm for forms authentication is that, as mentioned earlier, ASP.NET really uses HMACSHA1 (specifically the System.Security.Cryptography

.HMACSHA1 class). This means that the value of the validationKey attribute in <machineKey /> is used as part of the input to generate a SHA1 hash. As a result, for any attacker to force a hashing collision not only does an attacker have to force a collision with the SHA1 result, an attacker also has to guess the key that was used with HMACSHA1. Just brute forcing SHA1 isn’t sufficient, because an attacker needs to know the validationKey that was provided as input to the HMACSHA1 algorithm.

You can set the validationKey attribute of <machineKey /> to a maximum length of 128 characters, which represents a 64-byte key value. The minimum allowable length for valdationKey is 40 characters, which represents a 20-byte value. That means if you take advantage of the maximum allowable length, you have a 512 bit random value being used as the key, and an attacker has to somehow guess this value to create a viable hashing collision. I admit that I am definitely not a crypto-guru, so I can’t state how much stronger keying with HMACSHA1 is versus the plain SHA1 algorithm. However, with the added requirement of dealing with an unknown 512-bit key, the number of iterations necessary to force a collision with HMACSHA1 far exceeds either 2^69 or 2^80 iterations.

One final note: developers may use a little-known method in the forms authentication API —

FormsAuthentication.HashPasswordForStoringInConfigFile. In ASP.NET 1.1, this was a convenient way to obtain a hex-string representation of a hashed password using MD5 or SHA1. Although originally intended for making it easier to securely populate the <credentials /> section contained within <forms /> (since superseded by the more powerful and secure Membership feature in ASP.NET 2.0), customers have found this method handy as an easy-to-use interface to the hash algorithms. The problem today though is that with MD5’s strength in question, and now SHA1 potentially declining in strength, developers should really think about moving to SHA256 or SHA512 instead. However, the HashPasswordForStoringInConfigFile was not updated in ASP.NET 2.0 to support any of the other hash algorithms in the framework.

Instead, you will need to write code to accomplish what this method used to do (and I strongly encourage moving to other hashing algorithms over time even though it will take a little more work). To make the transition a bit easier, the following console sample below shows how to perform the equivalent functionality but with the extra option of specifying the desired hashing algorithm.

using System;

using System.Security.Cryptography; using System.Collections.Generic; using System.Text;

namespace HashPassword

{

class Program

{

static void Main(string[] args)

{

if ((args.Length < 2) || (args.Length > 2))

{

Console.WriteLine(“Usage: hashpassword password hashalgorithm”);

199

Chapter 5

return;

}

string password = args[0];

HashAlgorithm hashAlg = HashAlgorithm.Create(args[1]);

//Make sure the hash algorithm actually exists if (hashAlg == null)

{

Console.WriteLine(“Invalid hash algorithm.”); return;

}

string result = HashThePassword(password, hashAlg); Console.WriteLine(“The hashed password is: “ + result);

}

private static string HashThePassword(string password, HashAlgorithm hashFunction)

{

if (password == null)

throw new ArgumentNullException(“The password cannot be null.”);

byte[] bpassword = Encoding.UTF8.GetBytes(password);

byte[] hashedPassword = hashFunction.ComputeHash(bpassword);

//Transform the byte array back into hex characters StringBuilder s = new StringBuilder(hashedPassword.Length * 2); foreach (byte b in hashedPassword)

s.Append(b.ToString(“X2”));

return s.ToString();

}

}

}

The main entry point performs a few validations, the important one being the confirmation of the hash algorithm. You can indicate the hash algorithm using any of the string representations defined in the documentation for HashAlgorithm.Create method. As you would expect, you can use strings such as SHA1, SHA256, and SHA512. After the hash algorithm has been validated and created using the HashAlgorithm.Create method, the actual work is performed by the private HashThePassword method.

The password is converted to a byte representation because the hash algorithms operate off of byte arrays rather than strings. Calling ComputeHash on the hash object results in the new hashed value. Because you are probably hashing these values with the intent of storing them somewhere and retrieving the values later, the hashed value is converted back into a string where two hex characters are used to represent each byte value.

200

Forms Authentication

I have included a few sample results from running this utility:

D:\HashPassword\bin\Debug>HashPassword pass!word MD5

The hashed password is: 0033A636A8B61F9EE199AE8FA8185F2C

D:\HashPassword\bin\Debug>HashPassword pass!word SHA1

The hashed password is: 24151F57F8F9C408380A00CC4427EADD4DDEBFC6

D:\HashPassword\bin\Debug>HashPassword pass!word SHA256

The hashed password is:

DE98DD461F166808461A3CA721C41200A7982B7EB12F32C57C62572C6F2E5509

D:\HashPassword\bin\Debug>HashPassword pass!word SHA512 The hashed password is:

E84C057E3B6271ACC5EF6A8A81C55F2AB8506B7F464929417387BDC603E49BC0278DFAF063066A98EE0

74B15A956624B840DADBA65EDCF896521167C5DDE61CE

As you would expect, the strong SHA variants result in substantially longer hash values. The simplicity of the sample code shows how easy it is to start using stronger hash algorithms in your code. Because the utility generates hashed values, you can validate user-entered passwords later with similar code; just convert a user-entered password into either the hex string representation or byte representation of the hash value, and compare it against the hash value that was previously generated with the sample code. Also note that the sample code uses unkeyed hash algorithms. As a result, you will get the same hash values for a given piece of input text regardless of the machine you the utility on. This is because unkeyed hash algorithms apply the hash algorithm against the values you provide and do not inject any additional key material as is done with an algorithm like HMACSHA1.

New Encryption Options in ASP.NET 2.0

In ASP.NET 1.0 and 1.1, you could encrypt the forms authentication ticket with either DES or 3DES. Normally, most developers use 3DES because DES has already been cracked. 3DES, however, is considered to be an old encryption algorithm circa 2005. In 2001, the National Institute of Standards and Technology (NIST) published the details for a new common encryption standard called the Advanced Encryption Standard (AES). AES is the replacement for 3DES, and over time most application developers and companies will shift away from 3DES and start using AES.

ASP.NET 2.0 added support for AES so that developers can easily take advantage of the new encryption standard. AES has the benefit of supporting much longer keys than 3DES does. 3DES uses a 168-bit key (essentially three 56-bit keys), whereas AES supports key lengths of 128, 192, and 256 bits. To support the new encryption algorithm, ASP.NET 2.0 introduces a new configuration attribute in the <machineKey> section:

<machineKey ... decryption=[Auto|DES|3DES|AES] />

By default, the decryption attribute of <machineKey /> is set to Auto. In this case, ASP.NET 2.0 will look at the value in the decryptionKey attribute of <machineKey /> to determine the appropriate encryption algorithm. If a 16-character value is used for decryptionKey, ASP.NET 2.0 chooses DES as the encryption algorithm (16 hex characters equate to an 8-byte value, which is the number of bytes needed for a DES key). If a longer string of characters is set in decryptionKey, ASP.NET 2.0 chooses AES.

201