CSRF / XSRF в приложении ASP.NET MVC с кодом angularjs и jQuery? - PullRequest
0 голосов
/ 04 апреля 2019

В настоящее время я работаю над безопасностью CSRF / XSRF в некоторых старых приложениях ASP.NET MVC, в которых предшественники реализовали некоторые функции с использованием jQuery и в которых также есть элементы (компоненты), основанные на angularjs - так что вы можетевидим, что он имеет дело с технологическим сочетанием (стандартные представления / формы MVC, некоторые части, в которых используется jQuery $ .ajax, некоторые части пользовательского интерфейса пишутся с использованием компонентов angularjs + вызовы AJAX с использованием службы $ http) ...

Моя комплексная защита от CSRF в этом приложении была реализована путем создания класса ValidateAntiForgeryTokenAttributeEx, в котором метод OnAuthorization сначала проверяет наличие токена AntiForgery в заголовках HTTP-запроса и, если не найден, выполняет проверку по умолчанию, как в исходном классе ValidateAntiForgeryTokenAttribute:

// Based on https://github.com/aspnet/AspNetWebStack/blob/ea5b8854ade878237550b60f33d90c301d3f739c/src/System.Web.Mvc/ValidateAntiForgeryTokenAttribute.cs
// Modified to handle CSRF / XSRF checks for angularjs world ($http) / jQuery world ($.ajax) / traditional ASP.NET MVC world (form post to MVC controller with ValidateAntiForgeryToken).
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenAttributeEx : FilterAttribute, IAuthorizationFilter
{
    //...

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        // Validation only for HTTP methods other than GET, HEAD, TRACE, OPTIONS
        // See: https://github.com/aspnet/Mvc/blob/04ce6cae44fb0cb11470c21769d41e3f8088e8aa/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Filters/AutoValidateAntiforgeryTokenAuthorizationFilter.cs
        if (this.ShouldValidate(filterContext)) 
        {
            // Check request headers if it contains request validation token (case for $http in angularjs / $.ajax in jQuery world)
            // This solution is based on http://www.ojdevelops.com/2016/01/using-antiforgerytokens-in-aspnet-mvc.html
            string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get("X-XSRF-TOKEN");
            if (clientToken != null)
            {
                string serverToken = filterContext.HttpContext.Request.Cookies.Get(AntiForgeryConfig.CookieName)?.Value;
                if (string.IsNullOrEmpty(serverToken))
                    throw new HttpAntiForgeryException($"Cookies does not contain {AntiForgeryConfig.CookieName}.");

                AntiForgery.Validate(serverToken, clientToken);
            }
            else
            {
                // Execute default validation
                ValidateAction();
            }
        }
    }

    //
}

затем был добавлен глобальный фильтр:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // ...

        filters.Add(new ValidateAntiForgeryTokenAttributeEx());
    }
}

, поэтому нет необходимости добавлять ValidateAntiForgeryToken к каждому действию в нескольких контроллерах ASP.NET MVC...

Затем я изменил _Layout.cshtml, чтобы обработка клиентской части CSRF выполнялась в одном месте кода:

<body>
    <form>@Html.AntiForgeryToken()</form>
    <script type="text/javascript">
        var xsrfToken = $('input[name=__RequestVerificationToken]').val();                          // Get AntiForgery token generated by Html.AntiForgeryToken() ASP.NET MVC Razor HTML helper
        $.ajaxSetup({ headers: { 'X-XSRF-TOKEN': xsrfToken } });                                    // Set default header with AntiForgery token for all subsequent jQuery $.ajax requests
        document.cookie = 'XSRF-TOKEN=' + xsrfToken + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';    // See xsrfCookieName in angularjs sources; Alternative solution is to modify default headers for $httpProvider - see http://www.ojdevelops.com/2016/01/using-antiforgerytokens-in-aspnet-mvc.html
        Dropzone.prototype.defaultOptions.headers = { 'X-XSRF-TOKEN': xsrfToken };                  // see https://stackoverflow.com/questions/30149023/how-to-include-the-csrf-token-in-the-headers-in-dropzone-upload-request
    </script>

    <!-- ... -->
</body>

, поэтому у меня есть полное решение в одном месте кода клиентской стороны - он обрабатывает CSRFзащита для angualrjs $ http, для jQuery $ ajax и дополнительно для компонента Dropzone, также используемого в приложении.

Приведенное выше решение выглядит / работает нормально, , но я не уверен, используется ли "hack" для обработки angularjs CSRFправильно .Допустимо ли использовать cookie для передачи токена (созданного помощником MVC) в angularjs (angularjs НЕ берет токен CSRF со скрытого ввода , а из cookie )?Может быть, лучшее решение - установить заголовки по умолчанию для httpProvider?

// See http://www.ojdevelops.com/2016/01/using-antiforgerytokens-in-aspnet-mvc.html
var myApp = angular.module('myApp', []);

myApp.config(['$httpProvider', function ($httpProvider) {
    var antiForgeryToken = angular.element('input[name=__RequestVerificationToken]').val();
    $httpProvider.defaults.headers.post['__RequestVerificationToken'] = antiForgeryToken;
}]);

Я спрашиваю об этом, потому что ASP.NET также добавляет токен другого типа в куки (см. Куки с именем, начинающимся с __RequestVerificationToken), которыйиспользуется в процессе проверки ( MVC использует 2 токена для проверки - один из формы / заголовка и один из файлов cookie). Может быть, небезопасно иметь оба токена в куки?

Мне немного неясно, почему поддержка CSRF в angularjs основана на одном токене CSRF (считанном из куки), в то время как вВ ASP.NET MVC используются два токена - «основной» токен CSRF, который вместо файлов cookie читается из поля скрытой формы, и дополнительный токен в файлах cookie ...

...