AngularJS网络应用程序防伪令牌CSRF

wfypjpf4  于 2022-10-31  发布在  Angular
关注(0)|答案(3)|浏览(242)

我有一个由ASP.NET MVC应用程序托管的AngularJS单页应用程序(SPA)。
后端是ASP.NET Web应用程序
我希望通过在ASP.NET MVC部分中生成一个AntiForgeryToken,将其传递给AngularJS,然后让Web Api验证从后续AngularJS调用中接收的AntiForgeryToken来保护它免受CSRF攻击。
“跨站点请求伪造(CSRF)是一种攻击,它强制最终用户在当前已通过身份验证的Web应用程序上执行不需要的操作。CSRF攻击专门针对状态更改请求,而不是窃取数据,因为攻击者无法看到对伪造请求的响应。借助社会工程的一点帮助,(例如通过电子邮件或聊天发送链接),攻击者可诱使Web应用程序的用户执行攻击者选择的操作。如果受害者是普通用户,成功的CSRF攻击可强制用户执行状态更改请求,例如转帐、更改其电子邮件地址等。如果受害者是管理帐户,CSRF可能会危害整个Web应用程序。”

  • 开放式Web应用程序安全项目(OWASP)
t0ybt7op

t0ybt7op1#

添加到 * ASP.NET MVC**视图*(它为 AngularJS SPA 提供服务),比如Views\Home\Index.cshtml,它是生成AntiForgeryToken的HTML帮助程序。

@Html.AntiForgeryToken();

配置 AngularJS 以将上面生成的AntiForgeryToken作为 * 请求头 * 传递。

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

创建自定义 *Web API过滤器 * 以验证所有非GET请求(PUTPATCHPOSTDELETE)。
这假定您的所有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);
        }
    }
}

Global.asax.cs中,将新创建的筛选器注册为全局筛选器。

private static void RegisterWebApiFilters(HttpFilterCollection filters)
    {
        filters.Add(new WebApiValidateAntiForgeryTokenAttribute());
    }

或者,如果您不希望全局添加此筛选器,则可以仅将其放在某些Web API操作上,如下所示

[WebApiValidateAntiForgeryToken]

当然,从定义上讲,这是不太安全的,因为您总是有可能忘记将该属性应用到需要它的操作。
另外,请注意,您必须拥有Microsoft.AspNet.WebApi.Core套件,才能存取System.Web.Http命名空间。您可以透过NuGet安装Install-Package Microsoft.AspNet.WebApi.Core
这篇文章的灵感来源于this blog post

b4lqfgs4

b4lqfgs42#

__RequestVerificationToken添加到表单数据

var formData = new FormData();
    formData.append("__RequestVerificationToken", token);
    formData.append("UserName", $scope.kullaniciAdi);
    formData.append("Password", $scope.sifre);

    $http({
        method: 'POST',
        url: '/Login/Login',
        data: formData,
        transformRequest: angular.identity, 
        headers: { 'Content-Type': undefined }

    }).then(function successCallback(response) {

    }, function errorCallback(response) {

    });
7xllpg7q

7xllpg7q3#

添加到ASP.NET MVC视图

<Form ng-submit="SubmitForm(FormDataObject)">
        @Html.AntiForgeryToken()
        .....
        ...
        .
</Form>

然后在AngularJs控制器

angular.module('myApp', []).controller('myController', function ($scope, $http, $httpParamSerializerJQLike) {

        $scope.antiForgeryToken = angular.element('input[name="__RequestVerificationToken"]').attr('value');

        $scope.SubmitForm = function (formData) {
            var dataRequest = {
                __RequestVerificationToken: $scope.antiForgeryToken,
                formData: angular.toJson(formData)
            };

            $http.post("/url/...", $httpParamSerializerJQLike(dataRequest), { headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } }).then(function (response) {
                $scope.result = JSON.parse(response.data);
            });
        }
    });

为什么**$httpParamSerializerJQLike(dataRequest)**?因为如果不这样,AngularJs会将数据序列化为:

{__RequestVerificationToken: blablabla, formData: blablabla}

和Asp .NETMVC控制器引发所需的防伪窗体字段“__RequestVerificationToken”不存在错误。
但是,如果您使用**$httpParamSerializerJQLike(dataRequest)**序列化请求数据,则AngularJs将序列化为:

__RequestVerificationToken: blablabla
formData: blablabla

并且Asp .NETMVC控制器可以正确地识别该令牌。

相关问题