驗證視圖MAC失敗 Validation of ViewState MAC Failed
今天在調試Atlas時遇到錯誤:
驗證視圖MAC失敗。如果此引用程式由網路場或群集承載,請確保<machineKey>配置指定了相同的 validationKey 和驗證演算法。不能在群集中使用 AutoGenerate
發生錯誤的環境:
ASP.NET 2.0,使用Atlas的UpdatePanel,在UpdatePanel中動態載入使用者控制項,以達到動態更新頁面的效果。其中有一個使用者控制項中使用了GridView。當動態切換頁面時,出現上述錯誤。
問題分析:
經過一番搜尋,找到以下的文章:
http://aspadvice.com/blogs/joteke/archive/2006/02/02/15011.aspx
http://forums.asp.net/1173230/ShowPost.aspx
分析後找到了問題的根源。首先,文章中提到,如果用GridView,並且指定了DataKeyNames屬性,則出於安全的理由(因為DataKeyNames指定的欄位代表資料的主鍵,且該主索引值需要儲存在檢視狀態中發送到用戶端,使用者如果篡改主索引值,會導致安全問題),GridView會要求加密檢視狀態。為此會自動在頁面表單</forms>之前添加一個<input type="hidden" name="__VIEWSTATEENCRYPTED" id="__VIEWSTATEENCRYPTED" value="" /> 。
然而,Atlas的UpdatePanel要求放置在<form></form>內部,也就是</form>之前。這就意味著添加的隱藏input控制項沒有被放置在UpdatePanel內,而是放置在UpdatePanel和</form>之間。
當UpdatePanel更新時,UpdatePanel內部的控制項被提交到伺服器進行處理(Patrial Rendering),而整個頁面並沒有被提交。也就是說隱藏的input控制項沒有隨著一起提交。因此伺服器並不知道提交的ViewState被加密了,從而導致MAC驗證錯誤。
解決方案:
通過在Web.config裡邊添加
<pages enableEventValidation="false" viewStateEncryptionMode ="Never" />
可以解決該問題。
ASP.NET 2.0 and "Validation of ViewState Mac failed" exception
If you get this Exception
[HttpException (0x80004005): Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.]
and
- you know *for sure* that you aren't using a web farm
- it seems that it appears when using built-in databound controls such as GridView, DetailsView or FormView which utilize DataKeyNames.
- it appears if you have a large page which loads slowly for any reason
If following preconditions are true and you click a postbacking control/link while the Page hasn't loaded completely, you might get the "Validation of ViewState MAC failed" exception. In this case be sure to check following post on ASP.NET Forums where this has been discussed quite thoroughly : http://forums.asp.net/1173230/ShowPost.aspx
It appears because GridView using DataKeyNames requires ViewState to be encrypted. And when ViewState is encrypted, Page adds <input type="hidden" name="__VIEWSTATEENCRYPTED" id="__VIEWSTATEENCRYPTED" value="" /> field just before closing of the <form> tag. But this hidden field might not bet yet rendered to the browser with long-running pages, and if you make a postback before it "gets down", browser initiates postback without this field (in form post collection)
End result is that if this field is omitted on postback, Page doesn't "know" that viewstate is encrypted and causes the prementioned Exception. E.g Page expects to be fully-loaded before you can make a postback. And by the way similar problem is with event validation since __EVENTVALIDATION field is also rendered on the end of the form.
A way to overcome the problem is to set in web.config pages enableEventValidation="false" viewStateEncryptionMode ="Never" />Just note the security implications of these!
在預設狀況ASP.NET會隨機建立manchineKey當作驗證碼,
<configuration>
<system.web >
<machineKey validationKey = "AutoGenerate,IsolateApps"
decryptionKey = "AutoGenerate,IsolateApps" validation = "SHA1"/>
</system.web>
</configuration
若你有兩台Web Server,當另一台要驗證ViewState時,
validationKey或decryptionKey必須是相同的,所以必須自行產生validationKey後,
才能以相同的驗證碼來處理ViewState甚至cookie等。
產生驗證碼的方法如下:
string validationKey = GetKey(30); //20~64均可
string decryptionKey = GetKey(30); //20~64均可
protected string GetKey(int Keylen)
{
byte[] bytes = new byte[Keylen];
new RNGCryptoServiceProvider().GetBytes(bytes);
StringBuilder Builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
Builder.Append(string.Format("{0:X2}", bytes[i]));
}
return Builder.ToString();
}
產生後即可在每台Web Server網站的Web.Config加上<machineKey>即可,如下
<configuration>
<system.web>
<machineKey validationKey="3FF1E929BC0534950B0920A7B59FA698BD02DFE8"
decryptionKey="280450BB36319B474C996B506A95AEDF9B51211B1D2B7A77"
decryption="3DES" validation="SHA1"/>
</system.web>
</configuration>