jQuery Ajax调用和Html.AntiForgeryToken()
jQuery Ajax调用和Html.AntiForgeryToken()
我在我的应用程序中实现了对CSRF攻击的缓解,遵循了我在互联网上阅读的一些博客文章中的信息。特别是这些文章是我实现的驱动程序。
- ASP.NET MVC的最佳实践,来自ASP.NET和Web工具开发人员内容团队
- 跨站请求伪造攻击剖析,来自Phil Haack的博客
- ASP.NET MVC框架中的AntiForgeryToken - Html.AntiForgeryToken和ValidateAntiForgeryToken特性,来自David Hayden的博客
基本上这些文章和建议说,为了防止CSRF攻击,任何人都应该实现以下代码:
- 在接受POST Http动词的每个操作上添加
[ValidateAntiForgeryToken]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SomeAction( SomeModel model ) {
}
- 在提交数据到服务器的表单中添加
<%= Html.AntiForgeryToken() %>
帮助程序
无论如何,在我的应用程序的某些部分中,我正在使用jQuery通过Ajax POST向服务器发送数据,而没有任何表单。例如,当我让用户点击图像执行特定操作时,这种情况发生。
假设我有一个表格列出了一些活动。我在表格的某一列中放置了一个图像,上面写着“将活动标记为已完成”,当用户点击该活动时,我正在执行如以下示例的Ajax POST:
$("a.markAsDone").click(function (event) { event.preventDefault(); $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: {}, success: function (response) { // .... } }); });
在这种情况下我该如何使用 <%= Html.AntiForgeryToken() %>
? 我应该将帮助程序调用包括在 Ajax 调用的数据参数中吗?
对于文章篇幅较长,非常感谢您的耐心阅读。
编辑:
根据 jayrdub 的回答,我已经以以下方式使用了它:
$("a.markAsDone").click(function (event) { event.preventDefault(); $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: { AddAntiForgeryToken({}), id: parseInt($(this).attr("title")) }, success: function (response) { // .... } }); });
我喜欢360Airwalk提供的解决方案,但是它可能可以稍微改进一下。
第一个问题是,如果你使用空数据进行$.post()
,jQuery不会添加Content-Type
头信息,这种情况下ASP.NET MVC将无法接收并检查该令牌。因此,你必须确保头信息始终存在。
另一个改进是支持带内容的所有HTTP动词:POST,PUT,DELETE等。虽然你的应用程序可能只使用POST,但拥有一个通用的解决方案并验证你接收到的任何动词数据是否有防伪令牌是更好的。
$(document).ready(function () { var securityToken = $('[name=__RequestVerificationToken]').val(); $(document).ajaxSend(function (event, request, opt) { if (opt.hasContent && securityToken) { // handle all verbs with content var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken); opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam; // ensure Content-Type header is present! if (opt.contentType !== false || event.contentType) { request.setRequestHeader( "Content-Type", opt.contentType); } } }); });
我使用了一个简单的js函数,就像这样:
AddAntiForgeryToken = function(data) { data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val(); return data; };
由于页面上的每个表单都会有相同的令牌值,只需在最上面的主页面中添加以下内容:
<%-- used for ajax in AddAntiForgeryToken() --%>
然后在您的ajax调用中执行以下操作(已编辑以匹配您的第二个示例):
$.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }), success: function (response) { // .... } });