我遇到了反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
这是我在隐藏输入字段和post有效负载之间可以找到的唯一差异。
导致ValidateAntiForgeryToken
验证令牌失败的问题是什么?
问候。
答案 0 :(得分:77)
我最近已经和ValidateAntiForgeryToken
解决了几个问题,所以我会与你分享我的发现。
Salt :由于您提到这只发生在一个页面上,我最好的猜测是您在调用salt
和{{Html.AntiForgeryToken(salt)
时使用了不同的ValidateAntiForgeryToken(salt)
值1}}来电。
AJAX :正如另一个答案所说,使用AJAX可能需要额外的工作来确保令牌包含在POST中。这是我最喜欢的simple, automatic solution to add the token to all AJAX POST requests 但是,在您的问题中,您声明您已验证令牌正在发送。您是否确认过您只发送一次令牌?我发现我的一个AJAX调用正在发送令牌两次,这两个值组合在一起,导致它失败。
机器密钥和Cookie :这个问题很难看,容易发现(导致异常),但不是很直观。验证cookie和令牌使用唯一的“机器密钥”进行编码和解码。这意味着,如果您有服务器场或更改服务器,则您的cookie将不再有效。关闭浏览器可以解决问题(因为cookie是会话cookie)。但是,有些人长时间在后台打开浏览器窗口! 解决方案是在配置文件中设置“机器密钥”。这将告诉MVC在所有服务器上使用相同的密钥,确保cookie在任何地方都可以解密。
编码错误:使用名为jMeter的测试实用程序,我们尝试对页面进行加载测试,但发现它有一个错误导致我们的令牌有2个{{1}围绕价值。
解决方案是降低您对工具的信任!在浏览器中进行测试,如果可行,则创建一个提取令牌和cookie值的测试,并设置断点以验证结果。
如果这些内容都不适合您,那么我建议您查看the MVC source code for ValidateAntiForgeryTokenAttribute
,特别是"
方法。它将帮助您查看验证失败的不同步骤。您甚至可以检查错误OnAuthorization
以确定哪个部分失败。
作为旁注,我真的不喜欢MVC中Exception.StackTrace
的实现,因为:
ValidateAntiForgeryToken
并创建一个人工Page
来加密令牌和cookie。似乎过度杀伤。所以,我抓住了源代码,并创建了自己的专用子类,这对调试问题非常有帮助,因为我可以在验证方法上设置断点,并且很容易确定哪个验证步骤失败了。
答案 1 :(得分:4)
如果这是作为Ajax请求发送的,那么框架的当前设置不会自然地构建。
幸运的是Phil Haak写了一篇关于处理CSRF和Ajax的好文章 - &gt; Preventing CSRF With Ajax详细介绍了如何使用现有框架并对其进行修改以适用于Ajax / Json。
答案 2 :(得分:2)
根据我最近的调查结果......
如果您在ajax请求中将内容类型设置为“application / x-www-form-urlencoded”,则必须将AFRT放入数据中
如果您将内容类型设置为“application / json”,则令牌会进入ajax“headers”属性,如haack所述。
在服务器上,如果您正在检查表单类型令牌,那么使用vanilla AntiForgeryRequestTokenAttribute是正常的,但如果您想验证在标头中发送的令牌,那么您需要调用AntiForgeryToken.OnAuthorize ...或者其他任何内容,传递来自cookie的令牌(http上下文)。
这很容易,但如果是每个人都会这样做:)