34

我对反 CRSF MVC 机制有疑问。返回的 cookie 和表单输入不匹配。我每次都收到错误,仅在一个特定页面中。在应用程序的其余部分中,它运行良好。

服务器正在返回HTTP 500 Internal Server Error,我可以在日志中看到这个异常:

[System.Web.Mvc.HttpAntiForgeryException]:{“未提供所需的防伪令牌或无效。”}

这是服务器生成的隐藏输入:

<input name="__RequestVerificationToken" type="hidden" value="QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2+ZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io/0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld">

这是返回的 Cookie:

Set-Cookie:__RequestVerificationToken_L2VGbG93=skmTAVI8HCbfxDS+xhioIMIISL3UOBI7qJM1JbHjTtAqKl4W70pDUcTKMm0p3R3mrHDziE8vXw0C0OO4HArzWO1/e6py+v/cFdbe9maFgjl4jMiZ9Wc4YIhC6+IUXkk6yqJDJ8dCIr8qtGaYcD9IX+m7/SlVhu521KQSWJYRcaY=; path=/; HttpOnly

当我检查服务器发送的内容时,cookie 完全相同,但我认为有效负载具有不同的编码:

__RequestVerificationToken:QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2%2BZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io%2F0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld

区别在于出现编码的两个字符:

    /    ->   %2F  
    +    ->   %2B

这些是我能找到的隐藏输入字段和后有效载荷之间的唯一区别。

ValidateAntiForgeryToken导致验证令牌失败的问题可能是什么?

问候。

4

3 回答 3

79

我最近遇到并解决了几个问题ValidateAntiForgeryToken,所以我将与您分享我的发现。

Salt:由于您提到这只发生在单个页面上,我最好的猜测是您在调用和调用中使用了不同salt的值。Html.AntiForgeryToken(salt)ValidateAntiForgeryToken(salt)

AJAX:正如另一个答案所说,使用 AJAX 可能需要额外的工作以确保令牌包含在 POST 中。这是我最喜欢的将令牌添加到所有 AJAX POST 请求的简单、自动的解决方案
不过,在您的问题中,您声明您已验证令牌正在发送。您是否验证过您只发送一次令牌?我发现我的 AJAX 调用发送了两次令牌,合并了这些值,并导致它失败。

Machine Key and Cookies:这个问题很丑陋,很容易发现(导致异常),但不是很直观。验证 cookie 和令牌使用唯一的“机器密钥”进行编码和解码。这意味着如果您有服务器场,或更改您的服务器,您的 cookie 将不再有效。关闭浏览器可以解决问题(因为 cookie 是会话 cookie)。但是,有些人会让他们的浏览器窗口在后台打开很长时间!
解决方案是在配置文件中设置“机器密钥”。这将告诉 MVC 在所有服务器上使用相同的密钥,确保 cookie 在任何地方都可以解密。

编码错误:使用名为 jMeter 的测试实用程序,我们尝试对我们的页面进行负载测试,但发现它有一个错误导致我们的令牌"在值周围有 2 个额外的值。
解决方案是降低您对工具的信任!在浏览器中进行测试,如果可行,则创建一个提取令牌和 cookie 值的测试,并设置断点以验证结果。

如果这些东西都不适合您,那么我建议您查看MVC 源代码ValidateAntiForgeryTokenAttribute,特别是OnAuthorization方法。它将帮助您查看验证可能失败的不同步骤。您甚至可以检查您的错误Exception.StackTrace以确定哪个部分出现故障。

作为旁注,我真的不喜欢ValidateAntiForgeryToken在 MVC 中的实现,因为:

  • 大约有 5 个验证步骤可能会失败,但只有一个通用错误消息。
  • 该类是密封的,因此无法使用其他功能对其进行扩展。
  • 加密方法很奇怪——它初始化 aPage并创建一个人工ViewState来加密令牌和 cookie。似乎矫枉过正。

因此,我获取了源代码,并创建了自己的专门子类,结果证明这对调试它的问题也很有帮助,因为我可以在验证方法上设置断点,并且很容易确定哪个验证步骤失败了.

于 2011-10-24T05:58:30.467 回答
4

如果这是作为 Ajax 请求发送的,那么框架的当前设置不是自然而然地构建的。

幸运的是,Phil Haak 写了一篇关于处理 CSRF 和 Ajax ->使用 Ajax 预防 CSRF的精彩博客文章,其中详细介绍了如何使用现有框架并对其进行修改以适用于 Ajax/Json。

于 2011-10-21T09:18:50.750 回答
2

根据我最近的发现...

如果您在 ajax 请求中将内容类型设置为“application/x-www-form-urlencoded”,那么您必须将 AFRT 放入数据中

如果您将内容类型设置为“application/json”,那么令牌将进入 ajax“headers”属性,如 haack 所述。

在服务器上,如果您正在检查表单类型令牌,那么使用 vanilla AntiForgeryRequestTokenAttribute 是可以的,但是如果您想验证在标头中发送的令牌,那么您需要调用 AntiForgeryToken.OnAuthorize ... 或其他任何东西,从cookie(http 上下文)。

这并不容易,但如果是每个人都会这样做:)

于 2013-08-01T10:06:31.427 回答