Je me suis mise en œuvre d'un site web Angular.js qui frappe un ASP.NET WebAPI backend.
Angular.js a quelques fonctionnalités intégrées pour aider avec des anti-csrf de protection. Sur chaque requête http, il va chercher un cookie appelé "XSRF-TOKEN" et de le présenter comme un en-tête "X-XSRF-JETON" .
Cela repose sur le serveur web capable de définir la XSRF-JETON de cookie après l'authentification de l'utilisateur, puis la vérification de la X-XSRF-JETON-tête pour les requêtes entrantes.
L' Angulaire de la documentation états:
Pour bénéficier de cette fonction, votre serveur doit mettre un jeton dans un JavaScript lisible cookie de session appelé XSRF-JETON sur la première requête HTTP GET. Sur les non-demandes GET le serveur peut vérifier que le témoin correspond à X-XSRF-JETON en-tête HTTP, et donc être sûr que le code JavaScript s'exécutant sur votre nom de domaine pourrait avoir lu le jeton. 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 jeton est un condensé de votre site cookie d'authentification avec le sel pour plus de sécurité.
Je ne pouvais pas trouver tout de bons exemples de ce pour ASP.NET WebAPI, donc j'ai roulé mon propre avec l'aide de diverses sources. Ma question est, peut-on voir quelque chose de mal avec le code?
J'ai d'abord défini une simple classe helper:
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;
}
}
}
Puis j'ai la méthode suivante dans mon autorisation du contrôleur, et je l'appelle après j'appelle 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);
}
Ensuite, j'ai un attribut personnalisé qui je peux ajouter des contrôleurs de les faire vérifier le csrf en-tête:
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);
}
}
Enfin, je effacer le jeton Csrf lorsque l'utilisateur se déconnecte:
HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");
Quelqu'un peut-il repérer toute évidente (ou pas si évident) problèmes avec cette approche?