12/15/2006

Why does Forms Authentication Fail When Migrating from ASP.NET 1.1 To 2.0?

The <machineKey> element in the Web.config file is used to control tamper-proofing and encryption of ViewState, forms authentication tickets, and role cookies.

ViewState is signed and tamper-proof by default. You can request encryption for pages that contain sensitive items in their ViewState by using the ViewStateEncryptionMode attribute. Forms authentication and role cookies are also signed and encrypted by default. You do not need to modify the default settings under normal usage scenarios, except for a few situations that developers should be aware of:

If your application is in a Web farm or if you need to share authentication tickets across applications, you need to manually generate encryption and hashing keys and specify them in the <machineKey> element, and NOT use the "autogenerate" default.

If you migrate an application from ASP.NET 1.1 to ASP.NET 2.0 and use Hashed passwords, the key material used to generate your hashes WILL CHANGE. Again, the solution is to have specified encryption and decryption keys in your <machineKey> element, and to keep these the same in your migrated application.

Judging from the number of "what's wrong" posts around this subject, it appears that Microsoft hasn't made this clear enough. There are a couple of KB's on the subject, but the problem is - most developers read KB's AFTER they have a problem, not as a "preventative measure". You give me 100 developers who have installed Visual Studio 2005, and I will show you at least 95 developers who never read the "Readme" file that accompanies the distribution! It's just human nature to RTFM as a last resort.


For ViewState, a hashed message authentication code (HMAC) is generated from the ViewState content and the hash is compared on subsequent requests.
The validation attribute of the <machineKey> controls this, and indicates which hashing algorithm to use. It defaults to SHA1, which uses the HMACSHA1 algorithm. Valid choices for hashing include SHA1 or MD5, although SHA1 is preferable because it produces a larger hash and is considered cryptographically stronger than MD5. The validationKey attribute of <machineKey> is used in conjunction with the ViewState content to produce the HMAC. If your application is installed in a Web farm, you need to change the validationKey from AutoGenerate,IsolateApps to a specific manually generated key value.

Here is an example element with manually-provided keys:



<machineKey
validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
validation="SHA1"
decryption="AES"
/>


You can make yourself a nice little class to generate fresh keys, like so:


using System;

using System.Text;

using System.Security;

using System.Security.Cryptography;

 

public static class GenerateKey {

  public static string  GetKey( int keyLength)

  {

    int len = 128;

    if (keyLength  > 0)

      len = keyLength;

    byte[] buff = new byte[len/2];

    RNGCryptoServiceProvider rng = new

                            RNGCryptoServiceProvider();

    rng.GetBytes(buff);

    StringBuilder sb = new StringBuilder(len);

    for (int i=0; i<buff.Length; i++)

      sb.Append(string.Format("{0:X2}", buff[i]));  

      return sb.ToString();   

  }

}



Pass in the desired key length as the parameter to the GetKey method. Passing zero will result in the default 128 length.