ASP.net MVC通过AJAX防伪造请求的AntiForgeryToken

8

我目前正在开发一个ASP.net的MVC应用程序。我正在使用AJAX.ActionLink在记录列表中提供删除链接,但是这样做非常不安全。我已经添加了以下内容:

<AcceptVerbs(HttpVerbs.Post)>

在删除功能上,我们需要停止函数被简单的URL调用。然而,仍然存在另一个安全漏洞:如果我制作一个基本的HTML页面,包含以下内容:

<form action="http://foo.com/user/delete/260" method="post">
<input type="submit" />
</form>

它仍将执行一个帖子,但来自不同位置。

使用AntiForgeryToken可以与AJAX ActionLink一起使用吗?如果可以,这是一种安全的方法吗?我是否没有意识到更多的安全漏洞?

6个回答

9
请查看此博客文章
假设您有一个如下的Action方法:
``` [AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken] public ActionResult DeleteAccount(int accountId) { // delete stuff } ```
然后您通过以下方式调用它:
``` $.post('/home/DeleteAccount', { accountId: 1000 }, function() { alert('Account Deleted.'); }); ```
由于POST请求中没有包含AntiForgeryToken,因此会失败。
幸运的是,修复这个问题并不需要太多的脑力。所有客户端组件的AntiForgeryToken所做的就是将令牌放入基本隐藏字段中。因此,您只需要提取该数据并在AJAX调用中包含它即可。
``` var token = $('input[name=__RequestVerificationToken]').val(); ```
``` $.post('/home/DeleteAccount', { accountId: 1000, '__RequestVerificationToken': token }, function() { alert('Account Deleted.'); }); ```
请注意,如果页面上有多个表单和多个AntiForgeryTokens,您将需要在jQuery选择器中指定要使用哪个。另一个需要注意的地方是,如果您正在使用jQuery的serializeArray()函数,则需要以稍微不同的方式添加它:
``` var formData = $('#myForm').serializeArray(); var token = $('input[name=__RequestVerificationToken]').val(); formData.push({ name: '__RequestVerificationToken', value: token }); ```
``` $.post('/home/DeleteAccount', formData, function() { alert('Account Deleted.'); }); ```
更新:链接已修复。

您不需要使用 formData.push({ name: '__RequestVerificationToken', value: token });,因为 serializeArray() 方法会返回表单中的所有输入项,包括 __RequestVerificationToken 这个隐藏字段。 - Tien Do
是的,在 AJAX 提交时(使用当前的 MVC 4 和 MS 不显眼的 AJAX = jQuery)你确实需要这个! - Tony Wall

6

使用AntiForgeryToken与Ajax.ActionLink

除了jjwhite01的答案之外;为了将token插入表单数据,可以在Prefilter中使用option.data

$.ajaxPrefilter(
    function (options, localOptions, jqXHR) {
        if (options.type !== "GET") {
            var token = GetAntiForgeryToken();
            if (token !== null) {
                if (options.data.indexOf("X-Requested-With") === -1) {
                    options.data = "X-Requested-With=XMLHttpRequest" + (options.data === "") ? "" : "&" + options.data;
                }
                options.data = options.data + "&" + token.name + '=' + token.value;
            }
        }
    }
);

1
MVC 4 中不显眼的 AJAX 脚本包括 - options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" }) - 因此检查和添加已经不再必要。 - Brent

6
您可以在Ajax.ActionLink中使用AntiForgeryToken,但需要手动将AntiForgeryToken插入到请求的标头中,如下所示:
function GetAntiForgeryToken(){
   var tokenWindow = window;
   var tokenName = "__RequestVerificationToken";
   var tokenField = $(tokenWindow.document).find("input[type='hidden'][name='" +     tokenName +   "']");
   if (tokenField.length == 0) {return null;}
   else {
      return {
         name: tokenName,
         value: tokenField.val()
      };
   }
};

然后,我们可以使用$.ajaxPrefilter将其插入到标头中:
$.ajaxPrefilter(
   function (options, localOptions, jqXHR) {
      var token = GetAntiForgeryToken();
      jqXHR.setRequestHeader(token.name, token.value);
   }
);

我在这里写了一篇相关的文章,你可以点击链接查看。希望这可以帮到你!


1
这确实在标头中发送了令牌,但[ValidateAntiForgeryToken]过滤器要求在表单数据中包含此内容... - iGanja

2
我不了解 AJAX ActionLink 具体是什么,但从 WebForms 页面向 MVC action 发送带有 [AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken] 属性的请求是可能的。
您可以使用反射来获取用于设置 cookie 和匹配表单输入以进行 MVC 验证的 MVC 方法。
请参阅此答案:Using an MVC HtmlHelper from a WebForm

0

我自己没有使用过任何 Ajax 帮助程序,但我不认为您不能使用链接。我个人会使用 onload 事件处理程序从表单本身中不引人注目地创建一个链接,然后删除该表单。


0
为了借鉴$.ajaxPrefilter的答案,我将令牌添加到optionsoriginalOptions中,而不是jqXHR头部。这确实需要在页面上的某个表单中放置令牌。
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    var token = $('input[name="__RequestVerificationToken"]');
    if (token.length > 0) {
        var data = options.data;
        var dataArray = originalOptions.data;
        if (data && !data.includes('__RequestVerificationToken')) {
            options.data = data + '&__RequestVerificationToken=' + token.val();
        }
        if (dataArray && !('__RequestVerificationToken' in dataArray)) {
            var tokenObject = { name: '__RequestVerificationToken', value: token.val() };
            originalOptions.data.push(tokenObject);
        }
    }
});

请注意,这将向页面上的每个 AJAX 请求添加此令牌,因此您可能希望通过 options.url 字符串或 options.type == 'POST' 进行过滤。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接