10

Я пытаюсь установить Регистрация и Вход для участия в конкурсе Hot Towel SPA. Я создал SimpleMembershipFilters и ValidateHttpAntiForgeryTokenAttribute на основе asp.net single page application template.ValidateAntiForgeryToken с архитектурой SPA

Как получить код

@Html.AntiForgeryToken() 

работать в структуре Durandal SPA.

В настоящее время у меня есть register.html

<section> 
    <h2 data-bind="text: title"></h2> 

    <label>Firstname:</label><input data-bind="value: firstName" type="text" /> 
    <label>Lastname:</label><input data-bind="value: lastName" type="text" /> 
    <label>Email:</label><input data-bind="value: emailAddress" type="text" /> 
    <label>Company:</label><input data-bind="value: company" type="text" /> 
    <br /> 
    <label>Password:</label><input data-bind="value: password1" type="password" /> 
    <label>Re-Enter Password:</label><input data-bind="value: password2" type="password" /> 
    <input type="button" value="Register" data-bind="click: registerUser" class="btn" /> 
</section> 

register.js:

define(['services/logger'], function (logger) { 
    var vm = { 
     activate: activate, 
     title: 'Register', 
     firstName: ko.observable(), 
     lastName: ko.observable(), 
     emailAddress: ko.observable(), 
     company: ko.observable(), 
     password1: ko.observable(), 
     password2: ko.observable(), 
     registerUser: function() { 
      var d = { 
       'FirstName': vm.firstName, 
       'LastName': vm.lastName, 
       'EmailAddress': vm.emailAddress, 
       'Company': vm.company, 
       'Password': vm.password1, 
       'ConfirmPassword': vm.password2 
      }; 
      $.ajax({ 
       url: 'Account/JsonRegister', 
       type: "POST", 
       data: d , 
       success: function (result) { 
       }, 
       error: function (result) { 
       } 
      }); 
     }, 
    }; 


    return vm; 

    //#region Internal Methods 
    function activate() { 
     logger.log('Login Screen Activated', null, 'login', true); 
     return true; 
    } 
    //#endregion 
}); 

В АЯКС вызова $ как я прохожу AntiForgeryToken? Также как я могу создать токен?

ответ

7

Я читал this article о том, как использовать токены антифригации, используя javascript. Статья написана для WebApi, но она может легко применяться к контроллеру MVC, если вы хотите.

Короткий ответ что-то вроде этого: Внутри ваш взгляд cshtml:

<script> 
    @functions{ 
     public string TokenHeaderValue() 
     { 
      string cookieToken, formToken; 
      AntiForgery.GetTokens(null, out cookieToken, out formToken); 
      return cookieToken + ":" + formToken;     
     } 
    } 

    $.ajax("api/values", { 
     type: "post", 
     contentType: "application/json", 
     data: { }, // JSON data goes here 
     dataType: "json", 
     headers: { 
      'RequestVerificationToken': '@TokenHeaderValue()' 
     } 
    }); 
</script> 

Затем внутри контроллера asp.net необходимо проверить маркер следующим образом:

void ValidateRequestHeader(HttpRequestMessage request) 
{ 
    string cookieToken = ""; 
    string formToken = ""; 

    IEnumerable<string> tokenHeaders; 
    if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders)) 
    { 
     string[] tokens = tokenHeaders.First().Split(':'); 
     if (tokens.Length == 2) 
     { 
      cookieToken = tokens[0].Trim(); 
      formToken = tokens[1].Trim(); 
     } 
    } 
    AntiForgery.Validate(cookieToken, formToken); 
} 

Причина вы хотите передать его в заголовки, потому что, если вы передадите его как параметр данных параметра в свой вызов ajax, внутри запроса или тела запроса. Тогда вам будет сложнее получить токен противозачаточного средства для всех ваших различных сценариев. Потому что вам придется десериализовать тело, а затем найти токен. В заголовках его довольно последовательное и легко извлекаемое.


**** редактировать для луча **

Вот пример фильтра действий, который вы можете использовать, чтобы приписывать методы Web API для проверки, если antiforgerytoken предусмотрено.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Helpers; 
using System.Web.Http.Filters; 
using System.Net.Http; 
using System.Net; 
using System.Threading.Tasks; 
using System.Web.Http.Controllers; 
using System.Threading; 

namespace PAWS.Web.Classes.Filters 
{ 
    public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter 
    { 
     public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) 
     { 
      if (actionContext == null) 
      { 
       throw new ArgumentNullException("HttpActionContext"); 
      } 

      if (actionContext.Request.Method != HttpMethod.Get) 
      { 
       return ValidateAntiForgeryToken(actionContext, cancellationToken, continuation); 
      } 

      return continuation(); 
     } 

     private Task<HttpResponseMessage> FromResult(HttpResponseMessage result) 
     { 
      var source = new TaskCompletionSource<HttpResponseMessage>(); 
      source.SetResult(result); 
      return source.Task; 
     } 

     private Task<HttpResponseMessage> ValidateAntiForgeryToken(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) 
     { 
      try 
      { 
       string cookieToken = ""; 
       string formToken = ""; 
       IEnumerable<string> tokenHeaders; 
       if (actionContext.Request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders)) 
       { 
        string[] tokens = tokenHeaders.First().Split(':'); 
        if (tokens.Length == 2) 
        { 
         cookieToken = tokens[0].Trim(); 
         formToken = tokens[1].Trim(); 
        } 
       } 
       AntiForgery.Validate(cookieToken, formToken); 
      } 
      catch (System.Web.Mvc.HttpAntiForgeryException ex) 
      { 
       actionContext.Response = new HttpResponseMessage 
       { 
        StatusCode = HttpStatusCode.Forbidden, 
        RequestMessage = actionContext.ControllerContext.Request 
       }; 
       return FromResult(actionContext.Response); 
      } 
      return continuation(); 
     } 
    } 
} 
+0

Как вы называете 'ValidateRequestHeader'? –

+0

Я бы создал фильтр действий, потому что это проблема с перекрестным соединением и представляет собой ориентированный на аспект дизайн. Таким образом, вы можете просто указать методы, которые вы хотите применить для этой безопасности. –

+0

FYI: атрибут реализует 'IAuthorizationFilter', но отсутствует метод' public void OnAuthorization (AuthorizationContext filterContext) '. – Jaans

3

Grab значение маркера в JS вар

var antiForgeryToken = $('input[name="__RequestVerificationToken"]').val(); 

Затем просто добавить в заголовках POST AJAX в функции вызова .ajaxbeforeSend

beforeSend: function (xhr, settings) { 
      if (settings.data != "") { 
       settings.data += '&'; 
      } 
      settings.data += '__RequestVerificationToken=' + encodeURIComponent(antiForgeryToken); 
} 
+0

вход _RequestVerfitcationToken я должен добавить в мой код, но как и где можно установить значение входа? Это что-то, что автоматически генерирует? Извините за это. – jmogera

+1

Да, @ Html.AntiForgeryToken() создаст скрытый ввод автоматически, это то, что вы захватываете значение в js – curtisk

+0

Так как я использую шаблон HotTowel, который использует Durandal. Durandal следует архитектуре, где ищет файл .html и .js. Файл .html - это то, где мы имеем все элементы пользовательского интерфейса. Вызов @ здесь недоступен, поскольку он является файлом html staright, а не cshtml-файлом. – jmogera

1

Я боролся немного с этим, как ни один из существующих ответов, казалось, правильно работать в случае моего Durandal SPA приложения на основе Hot шаблона Полотенца.

Мне пришлось использовать комбинацию ответов Эвана Ларсона и курчика, чтобы получить что-то, что сработало так, как я полагаю.

В мой индекс.cshtml страница (Durandal поддерживает cshtml наряду с HTML), я добавил следующие чуть выше </body> тег

@AntiForgery.GetHtml(); 

Я добавил пользовательский класс фильтра, как предложил Эван Larson, однако мне пришлось изменить его, чтобы поддержать отрываясь значение куки отдельно и использовать __RequestVerificationToken как имя, а не RequestVerificationToken, поскольку это то, что предоставляется AntiForgery.GetHtml();

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Helpers; 
using System.Web.Http.Filters; 
using System.Net.Http; 
using System.Net; 
using System.Threading.Tasks; 
using System.Web.Http.Controllers; 
using System.Threading; 
using System.Net.Http.Headers; 

namespace mySPA.Filters 
{ 
    public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter 
    { 
     public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) 
     { 
      if (actionContext == null) 
      { 
       throw new ArgumentNullException("HttpActionContext"); 
      } 

      if (actionContext.Request.Method != HttpMethod.Get) 
      { 
       return ValidateAntiForgeryToken(actionContext, cancellationToken, continuation); 
      } 

      return continuation(); 
     } 

     private Task<HttpResponseMessage> FromResult(HttpResponseMessage result) 
     { 
      var source = new TaskCompletionSource<HttpResponseMessage>(); 
      source.SetResult(result); 
      return source.Task; 
     } 

     private Task<HttpResponseMessage> ValidateAntiForgeryToken(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) 
     { 
      try 
      { 
       string cookieToken = ""; 
       string formToken = ""; 
       IEnumerable<string> tokenHeaders; 
       if (actionContext.Request.Headers.TryGetValues("__RequestVerificationToken", out tokenHeaders)) 
       { 
        formToken = tokenHeaders.First(); 
       } 
       IEnumerable<CookieHeaderValue> cookies = actionContext.Request.Headers.GetCookies("__RequestVerificationToken"); 
       CookieHeaderValue tokenCookie = cookies.First(); 
       if (tokenCookie != null) 
       { 
        cookieToken = tokenCookie.Cookies.First().Value; 
       } 
       AntiForgery.Validate(cookieToken, formToken); 
      } 
      catch (System.Web.Mvc.HttpAntiForgeryException ex) 
      { 
       actionContext.Response = new HttpResponseMessage 
       { 
        StatusCode = HttpStatusCode.Forbidden, 
        RequestMessage = actionContext.ControllerContext.Request 
       }; 
       return FromResult(actionContext.Response); 
      } 
      return continuation(); 
     } 
    } 
} 

Впоследствии в моих App_Start/FilterConfig.cs Я добавил следующее

public static void RegisterHttpFilters(HttpFilterCollection filters) 
{ 
    filters.Add(new ValidateJsonAntiForgeryTokenAttribute()); 
} 

В Application_Start под моим Global.asax я добавил

FilterConfig.RegisterHttpFilters(GlobalConfiguration.Configuration.Filters); 

Наконец, для моих Ajax звонки Я добавил вывод входного поиска curtisk для добавления заголовка в мой запрос ajax, в случае запроса на вход.

var formForgeryToken = $('input[name="__RequestVerificationToken"]').val(); 

return Q.when($.ajax({ 
    url: '/breeze/account/login', 
    type: 'POST', 
    contentType: 'application/json', 
    dataType: 'json', 
    data: JSON.stringify(data), 
    headers: { 
     "__RequestVerificationToken": formForgeryToken 
    } 
})).fail(handleError); 

Это вызывает все мои почтовые просьбы требуется маркер проверки, который основывается на печенья и скрытой формой проверки токенов, созданных AntiForgery.GetHtml();

По моему мнению, это предотвратит потенциальные атаки на сценарии межсайтового сценария, так как атакующий сайт должен будет обеспечивать как куки-файл, так и значение скрытой формы, чтобы иметь возможность проверить себя, что было бы намного сложнее приобретать.

+0

Я использовал этот метод для моего приложения .. Но ее не работает – Niths

+0

мне нужно изменить, чтобы следовать, чтобы получить эту работу: Установите cookiename в AntiForgeryConfig.CookieName к __RequestVerificationToken (она называлась __RequestVerificationToken_fugh485 на моей системе) – Nicow

+0

Кроме того, tokenCookie .Cookies.First() Значение. дал мне неправильный cookie. Изменено tokenCookie.Cookies.First (c => c.Name == "__RequestVerificationToken"). Значение; – Nicow

0

При использовании MVC 5 прочтите это решение!

Я пробовал вышеуказанные решения, но они не работали для меня, фильтр действий не был достигнут, и я не мог понять, почему. Версия MVC не упоминалось выше, но я буду считать, что это версия 4. Я использую версию 5 в моем проекте, и в конечном итоге с помощью следующего фильтра действий:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 
using System.Web.Mvc.Filters; 

namespace SydHeller.Filters 
{ 
    public class ValidateJSONAntiForgeryHeader : FilterAttribute, IAuthorizationFilter 
    { 
     public void OnAuthorization(AuthorizationContext filterContext) 
     { 
      string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get(KEY_NAME); 
      if (clientToken == null) 
      { 
       throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME)); 
      } 

      string serverToken = filterContext.HttpContext.Request.Cookies.Get(KEY_NAME).Value; 
      if (serverToken == null) 
      { 
       throw new HttpAntiForgeryException(string.Format("Cookies does not contain {0}", KEY_NAME)); 
      } 

      System.Web.Helpers.AntiForgery.Validate(serverToken, clientToken); 
     } 

     private const string KEY_NAME = "__RequestVerificationToken"; 
    } 
} 

- Обратите внимание на using System.Web.Mvc и using System.Web.Mvc.Filters, а не http библиотеки (я думаю, что это одна из вещей, которые изменились с MVC v5 -..

Затем просто применить фильтр [ValidateJSONAntiForgeryHeader] к вашему действию (или контроллера), и он должен вызываться правильно

На моей странице макета прямо выше </body> У меня @AntiForgery.GetHtml();

Наконец, в моей странице Razor, я делаю вызов Ajax следующим образом:

var formForgeryToken = $('input[name="__RequestVerificationToken"]').val(); 

$.ajax({ 
    type: "POST", 
    url: serviceURL, 
    contentType: "application/json; charset=utf-8", 
    dataType: "json", 
    data: requestData, 
    headers: { 
    "__RequestVerificationToken": formForgeryToken 
    }, 
    success: crimeDataSuccessFunc, 
    error: crimeDataErrorFunc 
}); 
Смежные вопросы