Angular przeciwko Asp.Net WebApi, implementuj CSRF na serwerze

Implementuję stronę internetową w Angular.js, która uderza w backend ASPAP.

Angular.js ma kilka wbudowanych funkcji, które pomagają w ochronie anty-csrf. Podczas każdego żądania http będzie szukać pliku cookie o nazwie „XSRF-TOKEN” i przesyła go jako nagłówek o nazwie „X-XSRF-TOKEN”.

Polega to na tym, że serwer WWW może ustawić plik cookie XSRF-TOKEN po uwierzytelnieniu użytkownika, a następnie sprawdzić nagłówek X-XSRF-TOKEN dla żądań przychodzących.

TheDokumentacja kątowa stwierdza:

Aby to wykorzystać, serwer musi ustawić token w ciasteczkowym pliku cookie o nazwie XSRF-TOKEN na pierwszym żądaniu HTTP GET. Na kolejnych żądaniach innych niż GET serwer może sprawdzić, czy plik cookie pasuje do nagłówka HTTP X-XSRF-TOKEN, a zatem upewnij się, że tylko JavaScript działający w Twojej domenie mógł odczytać token. Token musi być unikalny dla każdego użytkownika i musi być możliwy do zweryfikowania przez serwer (aby zapobiec tworzeniu przez JavaScript własnych tokenów). Zalecamy, aby token był skrótem pliku cookie uwierzytelniania Twojej witryny z solą dla zwiększenia bezpieczeństwa.

Nie mogłem znaleźć żadnych dobrych przykładów tego dla ASP.NET WebAPI, więc wprowadziłem własne z pomocą różnych źródeł. Moje pytanie brzmi - czy ktoś widzi coś złego w kodzie?

Najpierw zdefiniowałem prostą klasę pomocnika:

public class CsrfTokenHelper
{
    const string ConstantSalt = "<ARandomString>";

    public string GenerateCsrfTokenFromAuthToken(string authToken)
    {
        return GenerateCookieFriendlyHash(authToken);
    }

    public bool DoesCsrfTokenMatchAuthToken(string csrfToken, string authToken) 
    {
        return csrfToken == GenerateCookieFriendlyHash(authToken);
    }

    private static string GenerateCookieFriendlyHash(string authToken)
    {
        using (var sha = SHA256.Create())
        {
            var computedHash = sha.ComputeHash(Encoding.Unicode.GetBytes(authToken + ConstantSalt));
            var cookieFriendlyHash = HttpServerUtility.UrlTokenEncode(computedHash);
            return cookieFriendlyHash;
        }
    }
}

Następnie mam w mojej kontrolerze autoryzacji następującą metodę i nazywam ją po wywołaniu FormsAuthentication.SetAuthCookie ():

    // http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks
    // http://docs.angularjs.org/api/ng.$http
    private void SetCsrfCookie()
    {
        var authCookie = HttpContext.Current.Response.Cookies.Get(".ASPXAUTH");
        Debug.Assert(authCookie != null, "authCookie != null");
        var csrfToken = new CsrfTokenHelper().GenerateCsrfTokenFromAuthToken(authCookie.Value);
        var csrfCookie = new HttpCookie("XSRF-TOKEN", csrfToken) {HttpOnly = false};
        HttpContext.Current.Response.Cookies.Add(csrfCookie);
    }

Następnie mam atrybut niestandardowy, który mogę dodać do kontrolerów, aby sprawdziły nagłówek csrf:

public class CheckCsrfHeaderAttribute : AuthorizeAttribute
{
    //  http://stackoverflow.com/questions/11725988/problems-implementing-validatingantiforgerytoken-attribute-for-web-api-with-mvc
    protected override bool IsAuthorized(HttpActionContext context)
    {
        // get auth token from cookie
        var authCookie = HttpContext.Current.Request.Cookies[".ASPXAUTH"];
        if (authCookie == null) return false;
        var authToken = authCookie.Value;

        // get csrf token from header
        var csrfToken = context.Request.Headers.GetValues("X-XSRF-TOKEN").FirstOrDefault();
        if (String.IsNullOrEmpty(csrfToken)) return false;

        // Verify that csrf token was generated from auth token
        // Since the csrf token should have gone out as a cookie, only our site should have been able to get it (via javascript) and return it in a header. 
        // This proves that our site made the request.
        return new CsrfTokenHelper().DoesCsrfTokenMatchAuthToken(csrfToken, authToken);
    }
}

Wreszcie usuwam token Csrf, gdy użytkownik się wylogowuje:

HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");

Czy każdy może dostrzec jakiekolwiek oczywiste (lub nie tak oczywiste) problemy z tym podejściem?

questionAnswers(4)

yourAnswerToTheQuestion