CryptographicException: 填充无效且无法移除,以及视图状态MAC验证失败

9
监控我的全局异常日志,这个错误似乎无论我怎么做都无法消除。我以为我终于摆脱了它,但它又回来了。您可以在类似的帖子中看到错误的堆栈跟踪。
环境说明:
IIS 6.0,.NET 3.5 SP1,单服务器ASP.NET应用程序
已经采取的步骤:
  <system.web>
    <machineKey validationKey="big encryption key"
      decryptionKey="big decryption key"
      validation="SHA1" decryption="AES" />

在我的页面基础中,为所有页面使用

标签。

  protected override void OnInit(EventArgs e)
  {
    const string viewStateKey = "big key value";

    Page.ViewStateUserKey = viewStateKey;
  }

我在页面源代码中发现所有由ASP.NET生成的隐藏字段都正确地位于页面顶部。


错误是一致的还是间歇性的? - Dan Story
极其间歇性,到目前为止从未能够再现。 - Chris Marisic
今天我在日志中再次看到了这条关于Web资源的消息,我发现一个机器人获取页面并将链接中的所有字符转换为小写,包括参数,所以这是不能正确获取编码字符串的又一个原因,并且会抛出类似“填充无效,无法移除”的消息。 - Aristos
3个回答

23

首先,让我们从一个事实开始,即这种视图状态错误发生在PostBack上

此外,我必须说,我已经做了所有人建议的事情来避免这个问题。而且我只有一台机器,但有两个运行相同页面的池。

所以有人会执行某个操作,可能是一个人或其他搜索引擎通过“点击”你的页面,或者一些黑客试图检查你的系统是否存在问题...

我也遇到过类似的问题(虽然罕见但确实存在),最终发现人们试图对我的页面进行黑客测试(来自同一IP的Dos攻击)。

我修改了函数LoadPageStateFromPersistenceMedium(),将其翻译视图状态,并记录输入的具体内容以及来自哪些IP...然后我开始监控这些结果,发现视图状态被手动更改或完全为空。

出现错误时,我只需将其重定向到相同的页面...

以下是我所做的...

public abstract class BasePage : System.Web.UI.Page
{
    protected override object LoadPageStateFromPersistenceMedium()
    {
        try
        {
            .. return the base, or make here your decompress, or what ever...
            return base.LoadPageStateFromPersistenceMedium();            
        }
        catch (Exception x)
        {
            string vsString = Request.Form[__VIEWSTATE];
            string cThePage = Request.RawUrl;

            ...log the x.ToString() error...
            ...log the vsString...
            ...log the ip coming from...
            ...log the cThePage...

        // check by your self for local errors
            Debug.Fail("Fail to load view state ! Reason:" + x.ToString());
        }

        // if reach here, then have fail, so I reload the page - maybe here you
        // can place somthing like ?rnd=RandomNumber&ErrorId=1 and show a message
        Responce.Redirect(Request.RawUrl, true);        

        // the return is not used after the redirect
        return string.Empty;
    }    
}

第二个原因

现在还有一个原因会导致这种情况发生,那就是因为有人在__EVENTVALIDATION加载之前点击了您的页面。

这个eventValidation被放置在asp.net找到的最后一个按钮事件上,如果您在页面的许多位置或靠近按钮的地方有一些按钮事件,那么这个eventValidation会移到页面末尾。

因此,即使您在页面顶部看到了viewstate,但验证在哪里?也许这从未被加载过-页面损坏了吗?用户点击速度太快了吗?

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" ... >

为了避免这种问题,我编写了一个简单的JavaScript代码,只有在输入框加载完成后才能按下按钮!!!
另外,__EVENTVALIDATION并不总是存在!因此,如果您要制作通用解决方案,最好不要搜索此字段,而是使用JavaScript技巧来检查整个页面是否已加载或其他您认为合适的内容。
以下是我的最终解决方案,使用jQuery实现:(请注意,我在PageLoad上检查eventvalidation是否存在!)。我把它放在了我的MasterPages中。
<script language="javascript" type="text/javascript">
    function AllowFormToRun()
    {
        var MyEventValidation = $("#__EVENTVALIDATION");

        if(MyEventValidation.length == 0 || MyEventValidation.val() == ""){
            alert("Please wait for the page to fully loaded.");
            return false;
        }

        return true; 
    }       
</script>

protected void Page_Load(object sender, EventArgs e)
{
    // I do not know if Page can be null - just in case I place it.
    if (Page != null && Page.EnableEventValidation)
    {
        Form.Attributes["onsubmit"] = "return AllowFormToRun();";
    }
}

你可以通过在页面按钮附近设置延迟来进行测试。
<% System.Threading.Thread.Sleep(5000); %>

更新

今天我在日志中再次看到WebResource的这条消息,并发现一只机器人获取页面并将链接中的所有字符都转为小写,包括参数。所以这是导致未正确编码字符串和出现填充无效且无法删除消息的更多原因之一。

希望这能对您有所帮助。


这很难选择哪个答案来接受。我选择了你的答案,因为原因#1。我从未考虑过在ASP.NET中用try / catch包装base.方法调用以在出现此类错误时接管执行,我认为这可能非常有用。第二个原因现在应该很少甚至不再是一个因素,在3.5框架中,他们通过使EventValidation字段插入到页面的非常高位置来解决了这个错误。这发生在我渲染任何内容之前。 - Chris Marisic
首先感谢您的投票。其次,我想说的是EventValidation有时会在页面底部。EventValidation需要读取一页中的所有事件,在某些情况下(至少在我的页面上),框架直到渲染页面的大部分内容之前才知道它...因此请查看您页面的源代码以检查EventValidation的位置。在我的页面上,它在底部或中间。 - Aristos
1
很好的建议,Aristos!我们(我)总是考虑使用现场或事后设备来诊断这些问题,但如果可行(关于对生产服务器的影响),在BasePage中捕获异常会减少很多猜测!谢谢& +1! - mjv

4
从使用多个错误消息关键字找到的网页调查中可以看出,这种类型的错误相对常见,通常是随机的(至少表面上是),不幸的是很少包括明确的解决方法或解释...
很多类似但又不同的情况可能与非常多不同的架构和基础配置有关,这些架构和配置可能导致密码层无法断言请求页中MAC(消息认证码)的真实性:
- 服务器农场设置 - 跨域/合作页面 - 第三方小部件库等 - 实际ASP程序逻辑(当然)
这些错误报告周围相对频繁的"标记"之一是提到资源请求(例如WebResource.axd)。请注意,此类请求通常不会被记录在日志文件中(以免将日志文件大小用于相对较少的事件膨胀)。这种从日志文件中缺失以及它们经常被缓存(因此bug相对随机和不经常发生)可能解释了这种bug的可能起源是如何"绕过检测"。这也表明,在尝试重新创建错误时(同时跟踪日志、实时等),防止Web浏览器缓存(或最初清除缓存)可能会有所帮助。
简而言之,以下是一些想法和需要寻找的东西:
- 开始记录*.axd请求 - 尝试将这样的axd请求与异常日志中的错误事件相关联 - 寻找具有资源引用的页面 - 如果在Farm设置中,请确保所有实例使用相同的密钥(显然,问题提示中提供的片段暗示了多个IIS服务器) - 对于带有第三方绑定的页面(搜索服务、会员计划...)应该持怀疑态度。
希望这能帮到你;-)

2

您确定您的问题与密码学有关,而不是由于ViewState过大引起的吗? 如果ViewState是问题所在,您可以对其进行分块 - 在web.config中更改pages/MaxPageStateFieldLength的值。


1
我从未见过提到这个错误可能是由于视图状态太大而引起的,但这似乎是一个非常容易重现的错误,那么我将尝试将一个巨大的对象插入到我的页面中,看看是否会导致这种情况。 - Chris Marisic

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