为什么即使使用静态机器密钥,HttpAntiForgeryException也会随机发生?

Dar*_*era 17 asp.net-mvc exception azure antiforgerytoken

我们有一个在Windows Azure(最新的2.x操作系统版本)上运行的ASP.NET MVC 2(.NET 4)应用程序,它有两个Web角色实例.

我们使用MVC提供的防伪令牌来处理所有POST请求,我们在web.config中设置了一个静态机器密钥,因此一切都可以在多台机器上运行并重启.99.9%的案例完美运作.

然而,我们偶尔会记录一个HttpAntiForgeryException,并显示消息"未提供所需的防伪标记或无效".

我知道问题可能是浏览器中不允许使用cookie,但我们已经验证了这些问题并启用了cookie并正确地来回发送.

各种浏览器出现错误,显然会给用户带来问题,因为他们必须重复操作,否则会丢失一些数据.可以这么说,我们无法在本地重现问题,但它只发生在Windows Azure上.

为什么会这样?我们怎能避免它?

Ed *_*ntz 17

我最近也碰到了这个,发现了两个原因.

1.浏览器恢复打开的上一个会话以进行缓存

如果您有一个可缓存的页面,可以向您的服务器执行帖子(即防伪程序将打开),并且用户将其浏览器设置为在启动时恢复上一个会话(此选项存在于chrome中),该页面将从缓存中呈现.但是,请求验证cookie不会存在,因为它是浏览器会话cookie,并在浏览器关闭时被丢弃.由于cookie消失了,你会得到防伪例外.解决方案:返回响应头,以便不缓存页面(即Cache-Control:private,no-store).

2.如果在启动您的站点时打开多个选项卡,则会出现竞争情况

浏览器可以选择在启动时打开一组选项卡.如果其中多个网站点击了您的网站,并返回了请求验证Cookie,那么您可能会遇到请求验证Cookie被覆盖的竞争条件.发生这种情况是因为多个请求从没有设置请求验证cookie的用户访问您的服务器.处理第一个请求并设置请求验证cookie.接下来处理第二个请求,但它没有发送cookie(在请求时尚未设置),因此服务器生成一个新请求.新的一个覆盖了第一个,现在该页面将在下一次执行帖子时获得防伪请求异常.MVC框架不处理这种情况.已向Microsoft的MVC团队报告此错误.


Dar*_*rov 7

防伪令牌包含当前连接的用户在发出时的用户名.在验证其有效性时,将检查当前连接的用户与发出令牌时使用的用户.因此,例如,如果您有一个表单,其中用户尚未经过身份验证并且您发出了防伪令牌,则不会存储任何用户名.如果您在提交表单时对用户进行身份验证,则令牌将不再有效.同样适用于注销.

以下是Validate方法的外观:

public void Validate(HttpContextBase context, string salt)
{
    string antiForgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(null);
    string str2 = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath);
    HttpCookie cookie = context.Request.Cookies[str2];
    if ((cookie == null) || string.IsNullOrEmpty(cookie.Value))
    {
        throw CreateValidationException();
    }
    AntiForgeryData data = this.Serializer.Deserialize(cookie.Value);
    string str3 = context.Request.Form[antiForgeryTokenName];
    if (string.IsNullOrEmpty(str3))
    {
        throw CreateValidationException();
    }
    AntiForgeryData data2 = this.Serializer.Deserialize(str3);
    if (!string.Equals(data.Value, data2.Value, StringComparison.Ordinal))
    {
        throw CreateValidationException();
    }
    string username = AntiForgeryData.GetUsername(context.User);
    if (!string.Equals(data2.Username, username, StringComparison.OrdinalIgnoreCase))
    {
        throw CreateValidationException();
    }
    if (!string.Equals(salt ?? string.Empty, data2.Salt, StringComparison.Ordinal))
    {
        throw CreateValidationException();
    }
}
Run Code Online (Sandbox Code Playgroud)

调试此方法的一种可能方法是从源代码重新编译ASP.NET MVC,并准确记录在抛出异常时输入的if情况.


Tom*_*Tom 0

您可以尝试以下几种选择。您可以尝试远程连接到计算机并查看事件日志,看看是否可以从中获取有关发生这种情况的更多信息。如果这没有帮助,您可以使用 DebugDiag 或其他一些工具来捕获进程的转储(DebugDiag 将允许您在发生此特定异常时捕获一个转储)。然后看看发生了什么。

如果您似乎无法从那里找出答案,您可以随时向 Microsoft 创建支持案例来帮助您进行调查。