我已经在我的应用程序中实现了对CSRF攻击的缓解,这是我在互联网上的一些博客文章上读到的信息。特别是这些帖子一直是我实现的驱动力

ASP的最佳实践。NET MVC从ASP。NET和Web工具开发人员内容团队 跨站点请求伪造攻击的剖析,来自Phil Haack的博客 ASP中的AntiForgeryToken。NET MVC框架- Html。来自David Hayden博客的AntiForgeryToken和ValidateAntiForgeryToken属性

基本上,这些文章和建议说,为了防止CSRF攻击,任何人都应该实现以下代码:

在接受POST Http谓词的每个操作上添加[ValidateAntiForgeryToken] (HttpPost) (ValidateAntiForgeryToken) SomeAction(SomeModel模型){ } 在向服务器提交数据的表单中添加<%= Html.AntiForgeryToken() %> helper

无论如何,在我的应用程序的某些部分,我做Ajax post与jQuery到服务器没有任何形式。例如,当我让用户点击图像来执行特定操作时就会发生这种情况。

假设我有一个包含活动列表的表。我在表的一列上有一个图像,说“标记活动已完成”,当用户单击该活动时,我正在做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调用的数据参数中包含helper调用?

很抱歉写了这么长时间,非常感谢你的帮助

编辑:

根据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) {
            // ....
        }
    });
});

当前回答

好吧,这里有很多帖子,没有一个对我有帮助,一天又一天的谷歌,仍然没有进一步的我得到的点wr-从头开始编写整个应用程序,然后我注意到这个小块在我的web .conf

 <httpCookies requireSSL="false" domain="*.localLookup.net"/>

现在我不知道为什么我添加了它,但我已经注意到,它在调试模式下被忽略,而不是在生产模式下(IE安装到IIS某处)

对我来说,解决方案是2个选项之一,因为我不记得我为什么添加它,我不能确定其他事情不依赖于它,第二域名必须全部小写,TLD不像我在*.localLookup.net中做的那样

也许有用,也许没有。我希望它能帮助到一些人

其他回答

除了我对@JBall的回答的评论之外,这是对我有用的最终答案。我正在使用MVC和Razor,我正在使用jQuery AJAX提交一个表单,所以我可以更新一些新的结果的部分视图,我不想做一个完整的回发(和页面闪烁)。

像往常一样在表单中添加@Html.AntiForgeryToken()。

我的AJAX提交按钮代码(即一个onclick事件)是:

//User clicks the SUBMIT button
$("#btnSubmit").click(function (event) {

//prevent this button submitting the form as we will do that via AJAX
event.preventDefault();

//Validate the form first
if (!$('#searchForm').validate().form()) {
    alert("Please correct the errors");
    return false;
}

//Get the entire form's data - including the antiforgerytoken
var allFormData = $("#searchForm").serialize();

// The actual POST can now take place with a validated form
$.ajax({
    type: "POST",
    async: false,
    url: "/Home/SearchAjax",
    data: allFormData,
    dataType: "html",
    success: function (data) {
        $('#gridView').html(data);
        $('#TestGrid').jqGrid('setGridParam', { url: '@Url.Action("GetDetails", "Home", Model)', datatype: "json", page: 1 }).trigger('reloadGrid');
    }
});

我保留了“success”动作,因为它显示了包含MvcJqGrid的部分视图是如何被更新的,以及它是如何被刷新的(非常强大的jqGrid网格,这是一个出色的MVC包装器)。

我的控制器方法是这样的:

    //Ajax SUBMIT method
    [ValidateAntiForgeryToken]
    public ActionResult SearchAjax(EstateOutlet_D model) 
    {
        return View("_Grid", model);
    }

我必须承认,我不喜欢将整个表单的数据作为模型发布,但如果您需要这样做,那么这是一种可行的方法。MVC只是让数据绑定太容易,而不是提交16个单独的值(或一个弱类型的FormCollection),这是可以的,我猜。如果你知道更好,请告诉我,因为我想产生健壮的MVC c#代码。

我知道还有很多其他的答案,但这篇文章非常简洁,迫使你检查所有的httppost,而不仅仅是其中的一些:

http://richiban.wordpress.com/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/

它使用HTTP报头,而不是试图修改表单集合。

服务器

//make sure to add this to your global action filters
[AttributeUsage(AttributeTargets.Class)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        var request = filterContext.HttpContext.Request;

        //  Only validate POSTs
        if (request.HttpMethod == WebRequestMethods.Http.Post)
        {
            //  Ajax POSTs and normal form posts have to be treated differently when it comes
            //  to validating the AntiForgeryToken
            if (request.IsAjaxRequest())
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value 
                    : null;

                AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute()
                    .OnAuthorization(filterContext);
            }
        }
    }
}

客户端

var token = $('[name=__RequestVerificationToken]').val();
var headers = {};
headers["__RequestVerificationToken"] = token;

$.ajax({
    type: 'POST',
    url: '/Home/Ajax',
    cache: false,
    headers: headers,
    contentType: 'application/json; charset=utf-8',
    data: { title: "This is my title", contents: "These are my contents" },
    success: function () {
        ...
    },
    error: function () {
        ...
    }
});

在这里我感觉自己像个高级的死灵法师,但这在MVC5四年后仍然是一个问题。

为了正确地处理ajax请求,需要在ajax调用时将防伪造令牌传递给服务器。将它集成到你的post数据和模型中是混乱和不必要的。将令牌作为自定义标头添加是干净且可重用的—您可以对其进行配置,这样就不必每次都记得这样做。

有一个例外- Unobtrusive ajax不需要ajax调用的特殊处理。令牌像往常一样在常规隐藏输入字段中传递。和普通POST完全一样。

_Layout.cshtml

在_layout。我有这个JavaScript块。它不将令牌写入DOM,而是使用jQuery从MVC Helper生成的隐藏输入文本中提取令牌。作为头名称的Magic字符串被定义为属性类中的常量。

<script type="text/javascript">
    $(document).ready(function () {
        var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
        //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative

        $.ajaxSetup({
            beforeSend: function (xhr) {
                if (!isAbsoluteURI.test(this.url)) {
                    //only add header to relative URLs
                    xhr.setRequestHeader(
                       '@.ValidateAntiForgeryTokenOnAllPosts.HTTP_HEADER_NAME', 
                       $('@Html.AntiForgeryToken()').val()
                    );
                }
            }
        });
    });
</script>

注意,在beforeSend函数中使用了单引号——呈现的输入元素使用双引号,这将破坏JavaScript文字。

客户端JavaScript

当它执行时,上面的beforeSend函数被调用,并且AntiForgeryToken被自动添加到请求头中。

$.ajax({
  type: "POST",
  url: "CSRFProtectedMethod",
  dataType: "json",
  contentType: "application/json; charset=utf-8",
  success: function (data) {
    //victory
  }
});

服务器库

需要自定义属性来处理非标准令牌。这建立在@viggity的解决方案上,但正确地处理了不显眼的ajax。这些代码可以隐藏在公共库中

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
    public const string HTTP_HEADER_NAME = "x-RequestVerificationToken";

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var request = filterContext.HttpContext.Request;

        //  Only validate POSTs
        if (request.HttpMethod == WebRequestMethods.Http.Post)
        {

            var headerTokenValue = request.Headers[HTTP_HEADER_NAME];

            // Ajax POSTs using jquery have a header set that defines the token.
            // However using unobtrusive ajax the token is still submitted normally in the form.
            // if the header is present then use it, else fall back to processing the form like normal
            if (headerTokenValue != null)
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value
                    : null;

                AntiForgery.Validate(cookieValue, headerTokenValue);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute()
                    .OnAuthorization(filterContext);
            }
        }
    }
}

服务器/控制器

现在只需将属性应用到Action。更好的是,你可以将属性应用到你的控制器,所有的请求都将被验证。

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}

我发现的解决方案不是针对ASPX,而是针对剃刀,但相当可行的问题。

我通过在请求中添加AntiForgery解决了这个问题。HTML Helper不会用调用创建HTML id

@Html.AntiForgeryToken()

为了将令牌添加到postrequest,我只是用jquery添加了AntiForgery id到隐藏字段:

$("input[name*='__RequestVerificationToken']").attr('id', '__AjaxAntiForgeryForm');

这导致控制器接受带有[ValidateAntiForgeryToken]属性的请求

1.定义函数从服务器获取令牌

@function
{

        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;                
        }
}

2.在发送到服务器之前获取令牌并设置报头

var token = '@TokenHeaderValue()';    

       $http({
           method: "POST",
           url: './MainBackend/MessageDelete',
           data: dataSend,
           headers: {
               'RequestVerificationToken': token
           }
       }).success(function (data) {
           alert(data)
       });

3.Onserver验证HttpRequestBase的方法你处理Post/get

        string cookieToken = "";
        string formToken = "";
        string[] tokens = Request.Headers["RequestVerificationToken"].Split(':');
            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
        AntiForgery.Validate(cookieToken, formToken);