并不总是创建__RequestVerificationToken

时间:2017-10-11 08:34:03

标签: asp.net-mvc azure antiforgerytoken

我们有一个包含多种表单的页面。每个人都有自己的@Html.AntiForgeryToken()。在我的本地机器上一切都很棒。

我们已部署到Azure(PAAS),但未在每个请求上创建__RequestVerificationToken 。有时它会在那里,有时我得到所需的防伪cookie不存在很少我得到令牌不匹配错误

此时我完全无能为力。我无法弄清楚我们的代码或Azure环境中是否存在问题?没有这些形式的ajax。

我们已将<machineKey>部分添加到 web.config 。没有缓存。有时它会在第一次出现在新设备上。

2 个答案:

答案 0 :(得分:2)

在花了大量时间进行调查后,使用Sentry和Azure Web服务器日志的组合,我发现了上述错误的两个主要原因:

1)在手机上,当浏览器在后台时,操作系统可能会突然停止以释放资源。发生这种情况时,通常会将页面存储在手机的驱动器上,并在重新打开浏览器后从那里重新加载。

然而,问题在于,到目前为止,作为会话cookie的Anti-Forgery Token已经过期,因为这实际上是一个新会话。因此,页面加载时没有使用Anti-Forgery Cookie,使用上一个会话中的HTML。这会导致The required anti-forgery cookie is not present例外。

2)虽然看似相关,但tokens do not match例外通常只与切线相关。原因似乎是用户行为同时打开多个标签。

只有当用户到达带有表单的页面时,才会分配Anti-Forgery Cookie。这意味着他们可以访问您的主页,并且没有防伪cookie。然后他们可以使用中键单击打开多个选项卡。多个选项卡是多个并行请求,每个请求都没有防伪cookie。

由于这些请求没有防伪cookie,因此对于每个请求,ASP.NET都会为其cookie生成一个单独的伪随机令牌,并在表单中使用它;但是,只保留收到的最后一个标题的结果。这意味着所有其他页面都会在页面上包含无效令牌,因为它们的防伪cookie已被覆盖。

对于解决方案,我创建了一个应该确保

的全局过滤器
  1. 即使页面没有表单,也会在任何页面上分配Anti-Forgery cookie,
  2. Anti-Forgery cookie不受会话限制。应该调整它的生命周期以匹配用户登录令牌,但是如果移动设备在没有会话的情况下重新加载页面,它应该在会话之间保持。
  3. 以下代码是FilterAttribute,必须在FilterConfig.cs内添加为全局过滤器。 请注意,虽然我不相信这会造成安全漏洞,但我绝不是安全专家,所以欢迎任何意见。

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AntiForgeryFilter : FilterAttribute, IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var cookie = filterContext.HttpContext.Request.Cookies.Get(AntiForgeryConfig.CookieName);
            var addCookie = true;
            if (string.IsNullOrEmpty(cookie?.Value))
            {
                cookie = filterContext.HttpContext.Response.Cookies.Get(AntiForgeryConfig.CookieName);
                addCookie = false;
            }
            if (string.IsNullOrEmpty(cookie?.Value))
            {
                AntiForgery.GetTokens(null, out string cookieToken, out string _);
                cookie = new HttpCookie(AntiForgeryConfig.CookieName, cookieToken)
                {
                    HttpOnly = true,
                    Secure = AntiForgeryConfig.RequireSsl
                };
            }
            cookie.Expires = DateTime.UtcNow.AddYears(1);
            if(addCookie) filterContext.HttpContext.Response.Cookies.Add(cookie);
        }
    
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
        }
    }
    

答案 1 :(得分:1)

我相信你的问题来自于在一个页面上使用不同的防伪标记的多个表单。 当请求页面时,您会在表单隐藏字段中获得两个不同的标记,但cookie中只有一个标记。 表单POST包含导致错误的不匹配标记。

如果页面只包含一个表单,请尝试AFT如何为您服务。如果它运作正常,那么我的假设是正确的。

here包含具有多个表单的页面的可能解决方案,但它有一些安全性缺陷,如This answer所述。

我不确定为什么一切都在你的本地主机上运行正常。 我已经创建了简单的应用程序,并尝试使用正确的Cookie令牌形式POST,但使用上一个会话中的旧表单令牌。令我惊讶的是,这样的POST成功通过。 在这种情况下,asp.net可能会对本地请求进行一些特殊处理。我还没有找到任何相关信息。

如果我的回答仍然无法帮助您,请提供以下数据以供进一步分析:

  1. 返回HTTP标头的原始页面请求,并形成防伪标记。
  2. 使用已发送的HTTP标头表单POST请求。