设置ViewStateUserKey导致出现“验证viewstate MAC失败”错误

16

我在我的BasePage类中有以下内容,所有我的ASPX页面都从这个类派生:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    ViewStateUserKey = Session.SessionID;
}

我在Web.config中设置了machineKey。我认为这个错误不是由于Web农场引起的,因为在我的开发机器上也会出现这个问题。

我的主机现在升级到了.NET 3.5 SP1。在这个更新之后,每次使用上面所述的ViewStateUserKey设置编译时,每次回传(postback)都会不断地出现“验证viewstate MAC失败”的错误消息。

我做错了什么?在最新的框架更新中,这个设置是否仍然必要?

5个回答

18

好的,我比较晚才参与这个讨论 - 但是这怎么能是正确答案呢?这只适用于已认证用户的情况,并且使用 ViewStateUserKey 作为用户名要比使用会话 ID GUID 更容易被猜测。

顺便说一下,如果您想要“修复”上面的代码,请使用会话 ID,但是您必须设置一个会话变量以防止会话 ID 每次都改变。例如:Session["Anything"] = DateTime.Now

ViewStateUserKey = Session.SessionID;

当然,这是基于您将使用会话的情况下,否则您需要使用其他密钥,例如用户名或保存在 cookie 中的任何其他 guid。


1
另外需要注意的是,有时候我会看到sessionid每次都会改变,直到会话被使用,而其他时候会话cookie似乎会立即发送给客户端而不使用会话。我还不确定其中的原因,但这只是我注意到的一些事情。 - Adam Tuliper
这非常有帮助。我不知道如果 ASP 中没有保存任何内容,它就不会保留会话。 - Reza

9
我已经搜索了很多资料,以找到问题的明确原因。Microsoft的这篇文章真正帮助解释了所有不同的原因。链接如下: http://support.microsoft.com/kb/2915218 。我们遇到的问题是无效的ViewStateUserKeyValue,这是第四个原因。
将ViewStateUserKey设置为Session.SessionID或User.Identity.Name对我们没有用。
我们偶尔会出现验证错误,原因如下。当IIS重置应用程序池时,会重新启动会话,从而导致错误。我们在登录时删除会话,以避免会话固定,也可能导致登录时出现错误。
最后对我们有用的是基于Cookie的解决方案,现在已提供在VS2012中。
public partial class SiteMaster : MasterPage
{
    private const string AntiXsrfTokenKey = "__AntiXsrfToken";
    private const string AntiXsrfUserNameKey = "__AntiXsrfUserName";
    private string _antiXsrfTokenValue;

    protected void Page_Init(object sender, EventArgs e)
    {
        //First, check for the existence of the Anti-XSS cookie
        var requestCookie = Request.Cookies[AntiXsrfTokenKey];
        Guid requestCookieGuidValue;

        //If the CSRF cookie is found, parse the token from the cookie.
        //Then, set the global page variable and view state user
        //key. The global variable will be used to validate that it matches in the view state form field in the Page.PreLoad
        //method.
        if (requestCookie != null
        && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
        {
            //Set the global token variable so the cookie value can be
            //validated against the value in the view state form field in
            //the Page.PreLoad method.
            _antiXsrfTokenValue = requestCookie.Value;

            //Set the view state user key, which will be validated by the
            //framework during each request
            Page.ViewStateUserKey = _antiXsrfTokenValue;
        }
        //If the CSRF cookie is not found, then this is a new session.
        else
        {
            //Generate a new Anti-XSRF token
            _antiXsrfTokenValue = Guid.NewGuid().ToString("N");

            //Set the view state user key, which will be validated by the
            //framework during each request
            Page.ViewStateUserKey = _antiXsrfTokenValue;

            //Create the non-persistent CSRF cookie
            var responseCookie = new HttpCookie(AntiXsrfTokenKey)
            {
                //Set the HttpOnly property to prevent the cookie from
                //being accessed by client side script
                HttpOnly = true,

                //Add the Anti-XSRF token to the cookie value
                Value = _antiXsrfTokenValue
            };

            //If we are using SSL, the cookie should be set to secure to
            //prevent it from being sent over HTTP connections
            if (FormsAuthentication.RequireSSL &&
            Request.IsSecureConnection)
            responseCookie.Secure = true;

            //Add the CSRF cookie to the response
            Response.Cookies.Set(responseCookie);
        }

            Page.PreLoad += master_Page_PreLoad;
        }

        protected void master_Page_PreLoad(object sender, EventArgs e)
        {
            //During the initial page load, add the Anti-XSRF token and user
            //name to the ViewState
            if (!IsPostBack)
            {
                //Set Anti-XSRF token
                ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey;

                //If a user name is assigned, set the user name
                ViewState[AntiXsrfUserNameKey] =
                Context.User.Identity.Name ?? String.Empty;
            }
            //During all subsequent post backs to the page, the token value from
            //the cookie should be validated against the token in the view state
            //form field. Additionally user name should be compared to the
            //authenticated users name
            else
            {
                //Validate the Anti-XSRF token
                if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue
                || (string)ViewState[AntiXsrfUserNameKey] !=
                (Context.User.Identity.Name ?? String.Empty))
            {
            throw new InvalidOperationException("Validation of
            Anti-XSRF token failed.");
            }
        }
    }
}

Source


请您详细说明一下?不要只提供链接。 - Kamiccolo
4
解释了为什么其他方案对我们不起作用,并将代码示例放在文章中。 - Vincejtl
我目前正在尝试您的解决方案,到目前为止它看起来表现良好。 - AFract
我没有深入检查过,但我想知道 web.config 文件中的可选部分 <machineKey validationKey.....> 是否确实可以做到我们期望的功能 :) - AFract
我对上述代码的主要问题是:我们是否不是通过将AntiXsrfTokenKey放入浏览器并从未更新它来执行类似于会话固定的操作?它能够持续一年,给恶意用户足够的时间编织新的攻击性回传表单吗? - Nickolodeon
@Nickolodeon 抱歉回复晚了。Cookie 的 expires 属性的默认值为 DateTime.MinValue。我们不设置 expires 值,实际上使 Cookie 成为会话 Cookie,并且不会持续一年,在每个会话上更新。 - Vincejtl

3

我现在已经通过改变代码来解决了这个问题:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    if (User.Identity.IsAuthenticated)
        ViewStateUserKey = User.Identity.Name;
}

1
@Druid,除了将上述代码放在基本页面的Oninit事件中之外,我这里也有同样的问题...我需要在任何子页面中设置viewstateuserkey吗?你能告诉我吗? - Glory Raj
据我所记,没有必要在每个页面上设置它。 - Druid

2
您可以使用EnableViewStateMac @Page属性关闭ViewState MAC编码吗?

是的,如果我这样做,它可以工作。如果ViewStateUserKey没有用处,我宁愿将其删除... - Druid

0
非常奇怪,我也遇到了类似的问题三天,现在我解决了它。 1. 我启用了表单身份验证,并将ssl设置为false。
<forms defaultUrl="~/" loginUrl="~/Account/Login.aspx" requireSSL="false" timeout="2880" />
  • 但是在我的httpcookies标签中,我设置了requireSSL=true。由于在Site.Master.cs中使用cookie来设置ViewStateUserKey,所以出现了问题。

  • 因此我一直收到错误信息。

  • 我将其修改为false并重新启动了Web应用程序,现在一切都很好。


  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接