В настоящее время я работаю над безопасностью 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 ...