Angulaire contre Asp.Net WebApi, mettre en œuvre CSRF sur le serveur

je mets en place un site Web en angle.js, qui frappe un ASP.NET WebAPI backend.

angulaire.js a quelques fonctionnalités intégrées pour aider à la protection anti-csrf. Sur chaque requête http, il cherchera un cookie appelé "XSRF-TOKEN" et le soumettra sous la forme d'un en-tête appelé "X-XSRF-TOKEN" .

cela suppose que le serveur web soit capable de configurer le cookie XSRF-TOKEN après l'authentification de l'utilisateur, puis de vérifier l'en-tête X-XSRF-TOKEN pour les demandes entrantes.

le documentation angulaire stipule:

pour en profiter, votre serveur doit configurer un token dans un cookie de session lisible en JavaScript appelé XSRF-TOKEN sur la première requête HTTP GET. Lors de requêtes ultérieures non-GET, le serveur peut vérifier que le cookie correspond à L'en-tête HTTP x-XSRF-TOKEN, et donc s'assurer que seul JavaScript tournant sur votre domaine aurait pu lire le token. Le jeton doit être unique pour chaque utilisateur et doit être vérifiable par le serveur (pour éviter le JavaScript de faire ses propres jetons). Nous recommandons que le token soit un condensé du cookie d'authentification de votre site avec du sel pour plus de sécurité.

Je ne pouvais trouver aucun bon exemple de cela pour ASP.NET WebAPI, donc j'ai roulé mon propre avec l'aide de diverses sources. Ma question Est-est-ce que quelqu'un peut voir quelque chose de mal avec le code?

J'ai d'abord défini un classe d'aide simple:

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

alors j'ai la méthode suivante dans mon contrôleur d'autorisation, et je l'appelle après avoir appelé FormsAuthentication.Setathcookie ():

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

alors j'ai un attribut personnalisé que je peux ajouter aux controllers pour les faire vérifier l'en-tête csrf:

public class CheckCsrfHeaderAttribute : AuthorizeAttribute
{
    //  /q/problems-implementing-validatingantiforgerytoken-attribute-for-web-api-with-mvc-4-rc-73379/".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);
    }
}

enfin, j'efface le token Csrf lorsque l'utilisateur se déconnecte:

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

est-ce que n'importe qui peut des problèmes évidents (ou pas si évidents) avec cette approche?

67
demandé sur vittore 2013-03-22 19:47:29

4 réponses

votre code semble correct. La seule chose est, vous n'avez pas besoin de la plupart du code que vous avez Comme web.api exécute "on top" de asp.net mvc, and latter a construit un support pour les jetons anti-contrefaçon.

dans les commentaires dbrunning et ccorrin expriment des préoccupations que vous ne pouvez utiliser les tokens build in AntiForgery que lorsque vous utilisez des helpers html MVC. Il n'est pas vrai. Les Helpers peuvent simplement exposer la paire de tokens basée sur la session que vous pouvez valider les uns par rapport aux autres. Voir ci-dessous pour détail.

mise à jour:

il y a deux méthodes que vous pouvez utiliser D'AntiForgery:

  • AntiForgery.GetTokens utilise deux paramètres de sortie pour retourner le token de cookie et le token de formulaire

  • AntiForgery.Validate(cookieToken, formToken) valide si la paire de jetons est valide

vous pouvez totalement réutiliser ces deux méthodes et utiliser formToken comme headerToken et cookieToken en vrai cookieToken. Puis il suffit d'appeler valider, tant à l'intérieur de l'attribut.

une autre solution consiste à utiliser JWT (cochez par exemple Membershippreboot implementation)

ce lien montre comment utiliser les jetons anti-contrefaçon construits avec ajax:

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


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

jetez également un oeil à cette question AngularJS ne peut pas trouver XSRF-Token cookie

6
répondu vittore 2017-05-23 10:31:11

je pense que votre code est défectueux. Toute l'idée autour de prevent CSRF est d'empêcher un token unique sur chaque requête, Pas chaque session. Si le jeton anti-contrefaçon est une valeur de session persistened, la capacité D'effectuer CSRF reste toujours. Vous devez fournir un jeton unique sur chaque demande...

0
répondu Kolchy 2016-04-07 02:40:28

cette solution n'est pas sécurisée puisque les attaques CSRF sont toujours possibles tant que le cookie Auth est valide. Le cookie auth et le cookie xsrf seront envoyés au serveur lorsqu'un attaquant vous fait exécuter une requête via un autre site, et vous êtes donc toujours vulnérable jusqu'à ce que l'utilisateur fasse une déconnexion "dure".

chaque demande ou session devrait avoir son propre token unique pour vraiment prévenir les attaques CRSF. Mais probablement la meilleure solution est de ne pas utiliser de cookie basé authentification mais authentification basée sur un jeton tel que OAuth. Cela empêche d'autres sites Web d'utiliser vos cookies pour effectuer des requêtes indésirables, puisque les tokens sont utilisés dans les en-têtes http au lieu de cookies. Et les en-têtes http ne sont pas envoyés automatiquement.

  1. authentification par Token ASP.NET Web API 2, Owin, and Identity
  2. AngularJS Token Authentication using ASP.NET Web API 2, Owin, et Identité

ces excellents billets de blog contiennent des informations sur la façon de mettre en œuvre OAuth for WebAPI. Les billets de blog contiennent également de grandes informations sur la façon de l'intégrer avec AngularJS.

une autre solution pourrait être de désactiver CORS et n'accepter que les requêtes entrantes provenant de domaines figurant sur la liste blanche. Cependant, cela ne fonctionnera pas pour les applications non-Web, comme les clients mobiles et/ou de bureau. À côté de cela une fois que votre site web est vulnérable à une attaque XSS, l'attaquant sera toujours en mesure de forger des requêtes pour votre compte.

0
répondu Dibran 2016-04-22 07:29:25

N'ont eu aucun problème signalé avec le code, donc je considère que la question a répondu.

-5
répondu dbruning 2015-01-07 04:53:26