Ich implementiere eine Website in Angular.js, die auf ein ASP.NET WebAPI-Backend trifft.
Angular.js verfügt über einige integrierte Funktionen, die den Anti-CSRF-Schutz unterstützen. Bei jeder http-Anfrage wird nach einem Cookie mit dem Namen "XSRF-TOKEN" gesucht und als Header mit dem Namen "X-XSRF-TOKEN" gesendet.
Dies setzt voraus, dass der Webserver das XSRF-TOKEN-Cookie nach der Authentifizierung des Benutzers setzen und dann den X-XSRF-TOKEN-Header auf eingehende Anforderungen überprüfen kann.
In der Angular-Dokumentation heißt es:
Um dies nutzen zu können, muss Ihr Server bei der ersten HTTP-GET-Anforderung ein Token in einem JavaScript-lesbaren Sitzungscookie namens XSRF-TOKEN setzen. Bei nachfolgenden Nicht-GET-Anforderungen kann der Server überprüfen, ob das Cookie mit dem HTTP-Header X-XSRF-TOKEN übereinstimmt, und daher sicherstellen, dass nur JavaScript, das in Ihrer Domäne ausgeführt wird, das Token gelesen haben kann. Das Token muss für jeden Benutzer eindeutig sein und vom Server überprüft werden können (um zu verhindern, dass das JavaScript seine eigenen Token erstellt). Wir empfehlen, dass das Token eine Zusammenfassung des Authentifizierungscookies Ihrer Site mit Salz für zusätzliche Sicherheit ist.
Ich konnte keine guten Beispiele für ASP.NET WebAPI finden, daher habe ich meine eigenen mit Hilfe aus verschiedenen Quellen erstellt. Meine Frage ist - kann jemand etwas falsch mit dem Code sehen?
Zuerst habe ich eine einfache Hilfsklasse definiert:
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;
}
}
}
Dann habe ich die folgende Methode in meinem Autorisierungscontroller und rufe sie auf, nachdem ich FormsAuthentication.SetAuthCookie () aufgerufen habe:
// 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);
}
Dann habe ich ein benutzerdefiniertes Attribut, das ich Controllern hinzufügen kann, damit sie den csrf-Header überprüfen:
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);
}
}
Zuletzt lösche ich das Csrf-Token, wenn sich der Benutzer abmeldet:
HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");
Kann jemand offensichtliche (oder nicht so offensichtliche) Probleme mit diesem Ansatz erkennen?