10

Próbuję ustawić rejestrację i zalogowanie się na wniosek Hot Towel SPA. Stworzyłem SimpleMembershipFilters i ValidateHttpAntiForgeryTokenAttribute na podstawie asp.net single page application template.ValidateAntiForgeryToken z architekturą SPA

Jak uzyskać kod

@Html.AntiForgeryToken() 

pracować w strukturze Durandal SPA.

Obecnie mam 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 
}); 

w $ wywołanie ajax jak mijam AntiForgeryToken? Jak również utworzyć token?

Odpowiedz

7

Chciałbym przeczytać this article o tym, jak korzystać z tokenów antywłamaniowych za pomocą javascript. Artykuł jest napisany dla WebApi, ale można go łatwo zastosować do kontrolera MVC, jeśli chcesz.

Krótka odpowiedź brzmi mniej więcej tak: Wewnątrz widoku 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> 

Następnie wewnątrz kontrolerze asp.net trzeba zweryfikować tokenu tak:

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); 
} 

Powodem chcesz go przekazać w nagłówkach, ponieważ jeśli przekażesz go jako parametr parametru danych w wywołaniu ajax, wewnątrz kwerendy lub treści żądania. Wówczas będzie ci trudniej zdobyć token przeciwnika za wszystkie twoje różne scenariusze. Ponieważ będziesz musiał zserializować ciało, a następnie znaleźć token. W nagłówkach jest bardzo spójny i łatwy do odzyskania.


**** edit dla ray **

Oto przykład z filtrem działania, które można wykorzystać do przypisania metod Web API do sprawdzania, czy antiforgerytoken jest świadczona.

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

Jak wywołujesz 'ValidateRequestHeader'? –

+0

Chciałbym utworzyć filtr akcji, ponieważ jest to problem przekrojowy i jest projektem zorientowanym na aspekt. W ten sposób możesz po prostu przypisać metody, które chcesz wymusić na tym zabezpieczeniu. –

+0

FYI: Atrybut implementuje "IAuthorizationFilter", ale brakuje metody "public void OnAuthorization (AuthorizationContext filterContext)". – Jaans

3

wartość Grab z tokena w JS var

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

Następnie wystarczy dodać do nagłówków POST ajax w funkcji wywołania .ajaxbeforeSend

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

wejście _RequestVerfitcationToken muszę dodać do mojego kodu, ale w jaki sposób i gdzie można ustawić wartość na wejściu? Czy to jest coś, co automatycznie generuje? Przepraszam za nowe. – jmogera

+1

Tak, @ Html.AntiForgeryToken() utworzy ukryte dane wejściowe automatycznie, to właśnie przechwycisz wartość w js – curtisk

+0

Ponieważ używam szablonu HotTowel, który używa Durandal. Durandal podąża za architekturą, w której szuka pliku .html i .js. Plik .html to miejsce, w którym mamy wszystkie elementy interfejsu użytkownika. Wywołanie @ nie jest tutaj dostępne, ponieważ jest to plik html staright, a nie cshtml. – jmogera

1

Walczyłem trochę z tego, ponieważ żadna z dotychczasowych odpowiedziach wydawało się działać prawidłowo w przypadku mojej aplikacji Durandal SPA na podstawie szablonu gorący ręcznik.

Musiałem użyć kombinacji odpowiedzi Evan Larson i curtisk, aby uzyskać coś, co działało tak, jak powinno.

Do mojego indeksu.Strona cshtml (Durandal obsługuje cshtml obok html) Dodałem następujące tuż nad tagiem </body>

@AntiForgery.GetHtml(); 

Dodałem niestandardową klasę filtra jak sugeruje Evan Larson, jednak musiałem zmodyfikować go wspierać patrząc na wartość cookie oddzielnie i używaj __RequestVerificationToken jako nazwy zamiast RequestVerificationToken, ponieważ jest to metoda dostarczana przez funkcję 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(); 
     } 
    } 
} 

Następnie w moich App_Start/FilterConfig.cs dodałem następujące

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

W Application_Start pod moim Global.asax dodałem

FilterConfig.RegisterHttpFilters(GlobalConfiguration.Configuration.Filters); 

koniec moich rozmów ajax Dodałem wyprowadzenie wyszukiwania wejściowego curtisk, aby dodać nagłówek do mojego żądania ajax, w przypadku żądania logowania.

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); 

Powoduje to, że wszystkie moje prośby pocztowych do wymagających token weryfikacyjny, który jest oparty na ciasteczko i weryfikacji forma ukryty żetonów stworzonych przez AntiForgery.GetHtml();

Według mojego doświadczenia pozwoli to uniknąć ataków typu cross site scripting, ponieważ strona atakująca będzie musiała dostarczyć zarówno wartość cookie, jak i ukrytą wartość formularza, aby móc się zweryfikować, co byłoby o wiele trudniejsze nabyć.

+0

użyłem tej metody dla mojej aplikacji .. Ale jej nie działa – Niths

+0

musiałem zmodyfikować wykonać, aby uzyskać to do pracy: Ustaw cookiename w AntiForgeryConfig.CookieName do __RequestVerificationToken (nazwano __RequestVerificationToken_fugh485 w moim systemie) – Nicow

+0

Również tokenCookie .Cookies.First(). Wartość; podał mi złe ciasteczko. Zmieniono na tokenCookie.Cookies.First (c => c.Nazwa == "__RequestVerificationToken"). Wartość; – Nicow

0

Jeśli używasz MVC 5 przeczytaj to rozwiązanie!

Próbowałem powyższych rozwiązań, ale nie działały one dla mnie, filtr działania nigdy nie został osiągnięty i nie mogłem zrozumieć dlaczego. Wersja MVC nie jest wymienione powyżej, ale mam zamiar założyć, że to wersja 4. Używam wersji 5 w moim projekcie, a zakończył się z następującym filtrem Działanie:

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"; 
    } 
} 

- Zanotuj using System.Web.Mvc i using System.Web.Mvc.Filters, a nie http biblioteki (myślę, że to jedna z tych rzeczy, które zmieniły z MVC v5 -..

Następnie wystarczy zastosować filtr [ValidateJSONAntiForgeryHeader] do działania (lub kontrolera) i powinno się nazywa poprawnie

Na mojej stronie układu tuż nad </body> Mam @AntiForgery.GetHtml();

Wreszcie w moją stronę Razor, robię wywołania AJAX następująco:

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 
});