在《ASP.NET虚拟主机中Forms Authentication的安全性》 中,我用同一台电脑的一个站点上创建的验证Cookie通过了另一个站点的表单验证,然后我得出一个结论:MachineKey的确只与Machine有关,而与WebApplication无关。在web.config文件<configuration>-<system.web>下的<machineKey>中可能的配置如下: <machineKey validationKey="AutoGenerate|value[,IsolateApps]" decryptionKey="AutoGenerate|value[,IsolateApps]" validation="SHA1|MD5|3DES"/> MSDN对IsolateApps的解释如下: If you add the IsolateApps modifier to the validationKey value, ASP.NET generates a unique encrypted key for each application using each application's application ID. 这就是说,加上",IsolateApps"以后每个Web应用程序所用的MachineKey就都不一样了,一个程序中生成的验证Cookie在另一个应用程序中就通不过了。那么我实验的结果是因为没有设置",IsolateApps"吗?我修改了两个Web站点的Web.config文件,分别加上了 <machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="SHA1" /> 结果再次出乎我的预料!其中一个站点生成的验证Cookie还是通过了另一个站点的验证。重新启动系统、更换用户名之类我都是过了,但结果没有改变。这与MSDN上说的可不一样。答案也只有用Reflector来寻找了。 找到了分析<machineKey>这一段的代码,位于System.Web.Configuration.MachineKey.MachineKeyConfig..ctor(Object, Object, XmlNode)。代码比较长,不过还是贴出来比较方便。
1
internal
MachineKeyConfig(
object
parentObject,
object
contextObject, XmlNode node)
2
...
{ 3 MachineKey.MachineKeyConfig config1 = (MachineKey.MachineKeyConfig) parentObject; 4 HttpConfigurationContext context1 = contextObject as HttpConfigurationContext; 5 if (HandlerBase.IsPathAtAppLevel(context1.VirtualPath) == PathLevel.BelowApp) 6 ... { 7 throw new ConfigurationException(HttpRuntime.FormatResourceString( " No_MachineKey_Config_In_subdir " ), node); 8 } 9 if (config1 != null ) 10 ... { 11 this ._ValidationKey = config1.ValidationKey; 12 this ._DecryptionKey = config1.DecryptionKey; 13 this ._ValidationMode = config1.ValidationMode; 14 this ._AutogenKey = config1.AutogenKey; 15 }16 XmlNode node1 = node.Attributes.RemoveNamedItem( " validationKey " ); 17 XmlNode node2 = node.Attributes.RemoveNamedItem( " decryptionKey " ); 18 int num1 = 0 ; 19 string [] textArray2 = new string [ 3 ] ... { " SHA1 " , " MD5 " , " 3DES " } ; 20 string [] textArray1 = textArray2; 21 XmlNode node3 = HandlerBase.GetAndRemoveEnumAttribute(node, " validation " , textArray1, ref num1); 22 if (node3 != null ) 23 ... { 24 this ._ValidationMode = (MachineKeyValidationMode) num1; 25 }26 HandlerBase.CheckForUnrecognizedAttributes(node);27 HandlerBase.CheckForChildNodes(node);28 if ((node1 != null ) && (node1.Value != null )) 29 ... { 30 string text1 = node1.Value; 31 bool flag1 = text1.EndsWith( " ,IsolateApps " ); 32 if (flag1) 33 ... { 34 text1 = text1.Substring( 0 , text1.Length - " ,IsolateApps " .Length); 35 }36 if (text1 == " AutoGenerate " ) 37 ... { 38 this ._ValidationKey = new byte [ 0x40 ]; 39 Buffer.BlockCopy(HttpRuntime.s_autogenKeys , 0 , this ._ValidationKey, 0 , 0x40 ); 40 }41 else 42 ... { 43 if ((text1.Length > 0x80 ) || (text1.Length < 40 )) 44 ... { 45 throw new ConfigurationException(HttpRuntime.FormatResourceString( " Unable_to_get_cookie_authentication_validation_key " , text1.Length.ToString()), node1); 46 }47 this ._ValidationKey = MachineKey.HexStringToByteArray(text1); 48 if ( this ._ValidationKey == null ) 49 ... { 50 throw new ConfigurationException(HttpRuntime.FormatResourceString( " Invalid_validation_key " ), node1); 51 }52 }53 if (flag1) 54 ... { 55 int num2 = SymbolHashCodeProvider.Default.GetHashCode(HttpContext.Current.Request.ApplicationPath ); 56 this ._ValidationKey[ 0 ] = ( byte ) (num2 & 0xff ); 57 this ._ValidationKey[ 1 ] = ( byte ) ((num2 & 65280 ) >> 8 ); 58 this ._ValidationKey[ 2 ] = ( byte ) ((num2 & 16711680 ) >> 0x10 ); 59 this ._ValidationKey[ 3 ] = ( byte ) ((num2 & 4278190080 ) >> 0x18 ); 60 }61 }62 if (node2 != null ) 63 ... { 64 string text2 = node2.Value; 65 bool flag2 = text2.EndsWith( " ,IsolateApps " ); 66 if (flag2) 67 ... { 68 text2 = text2.Substring( 0 , text2.Length - " ,IsolateApps " .Length); 69 }70 if (text2 == " AutoGenerate " ) 71 ... { 72 this ._DecryptionKey = new byte [ 0x18 ]; 73 Buffer.BlockCopy(HttpRuntime.s_autogenKeys , 0x40 , this ._DecryptionKey, 0 , 0x18 ); 74 this ._AutogenKey = true ; 75 }76 else 77 ... { 78 this ._AutogenKey = false ; 79 if (text2.Length == 0x30 ) 80 ... { 81 TripleDESCryptoServiceProvider provider1 = null ; 82 try 83 ... { 84 provider1 = new TripleDESCryptoServiceProvider(); 85 }86 catch (Exception) 87 ... { 88 }89 if (provider1 == null ) 90 ... { 91 throw new ConfigurationException(HttpRuntime.FormatResourceString( " cannot_use_Triple_DES " ), node2); 92 }93 }94 if ((text2.Length != 0x30 ) && (text2.Length != 0x10 )) 95 ... { 96 throw new ConfigurationException(HttpRuntime.FormatResourceString( " Unable_to_get_cookie_authentication_decryption_key " , text2.Length.ToString()), node2); 97 }98 this ._DecryptionKey = MachineKey.HexStringToByteArray(text2); 99 if ( this ._DecryptionKey == null ) 100 ... { 101 throw new ConfigurationException(HttpRuntime.FormatResourceString( " Invalid_decryption_key " ), node2); 102 }103 }104 if (flag2 ) 105 ... { 106 int num3 = SymbolHashCodeProvider.Default.GetHashCode(HttpContext.Current.Request.ApplicationPath ); 107 this ._DecryptionKey[ 0 ] = ( byte ) (num3 & 0xff ); 108 this ._DecryptionKey[ 1 ] = ( byte ) ((num3 & 65280 ) >> 8 ); 109 this ._DecryptionKey[ 2 ] = ( byte ) ((num3 & 16711680 ) >> 0x10 ); 110 this ._DecryptionKey[ 3 ] = ( byte ) ((num3 & 4278190080 ) >> 0x18 ); 111 }112 }113 }
114
这段程序从配置文件中分析了validationKey和decryptionKey,他们两个的处理过程很相似。第16行node1是validationKey,第28行开始对其进行分析。第31行bool flag1 = text1.EndsWith(",IsolateApps");用flag1表示是否有",IsolateApps"。下面从36至52行都没用到flag1。53-60是关键。看到这里就明白了,MSDN中所说的“application's application ID”其实就是HttpContext.Current.Request.ApplicationPath,不同的ApplicationPath所生成的validationKey和decryptionKey就不一样。 因为我上面做实验是用的两个不同的WebSite,当然也是不同的WebApplication,但由于两个WebApplication的ApplicationPath都是"/",所以它们的validationKey和decryptionKey是相同的!要是在相同的WebSite建立不同的虚拟目录并配置成应用程序,所生成的Cookie就不通用了。我也做了实验,证实了这一点,步骤就不用说了。 这也验证了我在《ASP.NET虚拟主机中Forms Authentication的安全性》 中得到的结论,在虚拟主机环境中使用表单验证时很容易被同一台电脑上的其它WebApplication所欺骗。虽然validationKey和decryptionKey还可以在web.config中自定义,可是有多少虚拟主机提供商对不同的WebApplication用了不同的用户运行,并且对每个站点目录配置了恰当的NTFS权限呢?
------------------------------------------------------
测试环境:Windows 2003, .NET Framework 1.1 sp1(--2005-7-27补充)