Подтвердить что ты не робот

AngularJS Web Api AntiForgeryToken CSRF

У меня есть приложение AngularJS для одиночной страницы (SPA), размещаемое приложением ASP.NET MVC.
Внутренний сервер ASP.NET Web Api.

Я бы хотел защитить его от атак CSRF, создав AntiForgeryToken в части ASP.NET MVC, передав ее AngularJS. > , а затем Web Api подтвердите AntiForgeryToken, полученные от последующих вызовов AngularJS.

"Подделка запросов на межсайтовый запрос (CSRF) - это атака, которая заставляет пользователя для выполнения нежелательных действий в веб-приложении, в котором они находятся в настоящее время аутентифицирован. Специальные атаки CSRF запросы, изменяющие состояние, а не кражу данных, поскольку у злоумышленника нет способ увидеть ответ на поддельный запрос. С небольшой помощью социальная инженерия (например, отправка ссылки по электронной почте или чату), злоумышленник может обмануть пользователей веб-приложения в выполнение действия выбора злоумышленника. Если жертва является обычным пользователем, успешная атака CSRF может заставить пользователя выполнить изменение состояния запросы, такие как перевод средств, изменение их адреса электронной почты и т.д. вперед. Если жертва является административной учетной записью, CSRF может пойти на компромисс всего веб-приложения".
- Открытый проект безопасности веб-приложений (OWASP)

4b9b3361

Ответ 1

Добавить в ASP.NET MVC Просмотр, который обслуживает AngularJS SPA, скажем Views\Home\Index.cshtml, HTML-помощник, который генерирует AntiForgeryToken.

@Html.AntiForgeryToken();

Настройте AngularJS, чтобы передать выше сформированный AntiForgeryToken в качестве заголовка запроса.

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

Создайте собственный фильтр веб-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);
        }
    }
}

Зарегистрируйте вновь созданный фильтр как глобальный, в Global.asax.cs.

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

В качестве альтернативы, если вы не хотите добавлять этот фильтр глобально, вы можете поместить его только в определенные действия веб-API, например, это

[WebApiValidateAntiForgeryToken]

Это, конечно, по определению менее безопасно, так как всегда есть шанс, что вы забудете применить этот атрибут к нужному ему действию.

Также обратите внимание, что для доступа к пространству имен System.Web.Http у вас должен быть пакет Microsoft.AspNet.WebApi.Core. Вы можете установить его через NuGet с помощью Install-Package Microsoft.AspNet.WebApi.Core.

Этот пост был сильно вдохновлен в этом сообщении в блоге.

Ответ 2

Добавить в ASP.NET MVC View

<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.NET MVC выбрасывает Требуемое поле формы для защиты от подделки "__RequestVerificationToken" отсутствует.

Но если вы сериализуете данные запроса с помощью $httpParamSerializerJQLike (dataRequest), AngularJs сериализуется как:

__RequestVerificationToken: blablabla
formData: blablabla

и контроллер Asp.NET MVC может распознавать токен без каких-либо ошибок.