AngularJS Web Api AntiForgeryToken CSRF

Mih*_*scu 37 c# asp.net-mvc asp.net-web-api angularjs

我有一个由ASP.NET MVC应用程序托管的AngularJS单页面应用程序(SPA). 后端是ASP.NET Web Api.

我想通过在ASP.NET MVC部分中生成一个,将其传递给AngularJS,然后让Web Api验证从后续AngularJS调用接收到的内容来保护它免受CSRF攻击.AntiForgeryTokenAntiForgeryToken

"跨站点请求伪造(CSRF)是一种攻击,它会强制最终用户在当前对其进行身份验证的Web应用程序上执行不需要的操作.CSRF攻击专门针对状态更改请求,而不是数据被盗,因为攻击者无法查看对伪造请求的响应.通过社交工程的一些帮助(例如通过电子邮件或聊天发送链接),攻击者可以欺骗Web应用程序的用户执行攻击者选择的操作.如果受害者是普通用户,则成功的CSRF攻击可以强制用户执行状态更改请求,例如转移资金,更改其电子邮件地址等.如果受害者是管理帐户,CSRF可能会破坏整个Web应用程序."
- 开放式Web应用程序安全项目(OWASP)

Mih*_*scu 56

添加到为AngularJS SPA提供服务的ASP.NET MVC 视图中,比如生成的HTML帮助器 .Views\Home\Index.cshtmlAntiForgeryToken

@Html.AntiForgeryToken();
Run Code Online (Sandbox Code Playgroud)

配置AngularJS通过产生上述AntiForgeryToken请求报头.

angular.module('app')
.run(function ($http) {
    $http.defaults.headers.common['X-XSRF-Token'] =
        angular.element('input[name="__RequestVerificationToken"]').attr('value');
});
Run Code Online (Sandbox Code Playgroud)

创建自定义的Web API过滤器来验证所有非GET请求(PUT,PATCH,POST,DELETE).

这假设您的所有GET请求都是安全的,不需要保护.
如果不是这样,请删除if (actionContext.Request.Method.Method != "GET")排除.

using System;
using System.Linq;
using System.Net.Http;
using System.Web.Helpers;
using System.Web.Http.Filters;

namespace Care.Web.Filters
{
    public sealed class WebApiValidateAntiForgeryTokenAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(
            System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            if (actionContext.Request.Method.Method != "GET")
            {
                var headers = actionContext.Request.Headers;
                var tokenCookie = headers
                    .GetCookies()
                    .Select(c => c[AntiForgeryConfig.CookieName])
                    .FirstOrDefault();

                var tokenHeader = string.Empty;
                if (headers.Contains("X-XSRF-Token"))
                {
                    tokenHeader = headers.GetValues("X-XSRF-Token").FirstOrDefault();
                }

                AntiForgery.Validate(
                    tokenCookie != null ? tokenCookie.Value : null, tokenHeader);
            }

            base.OnActionExecuting(actionContext);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将新创建的过滤器注册为全局过滤器Global.asax.cs.

    private static void RegisterWebApiFilters(HttpFilterCollection filters)
    {
        filters.Add(new WebApiValidateAntiForgeryTokenAttribute());
    }
Run Code Online (Sandbox Code Playgroud)

或者,如果您不希望全局添加此过滤器,则只能将其放在某些Web API操作上,如下所示

[WebApiValidateAntiForgeryToken]
Run Code Online (Sandbox Code Playgroud)

当然,这在定义上不太安全,因为总是有可能忘记将属性应用于需要它的动作.

另请注意,您必须拥有该Microsoft.AspNet.WebApi.Core包才能访问System.Web.Http命名空间.你可以通过NuGet安装它Install-Package Microsoft.AspNet.WebApi.Core.

这篇文章深受这篇博文的启发.

  • 对不起这个问题,但通常的做法是立即提出问题并准备好答案? (3认同)
  • 是.您可以在此处阅读更多相关信息:https://blog.stackexchange.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/. (3认同)
  • @onefootswill如果你有多个表单,你应该只生成一次令牌.它应该服务于*请求*而不是单独的形式. (2认同)