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?
quelle
Antworten:
Ihr Code scheint in Ordnung zu sein. Das einzige ist, dass Sie den größten Teil des Codes, den Sie haben, nicht benötigen, da web.api "über" asp.net mvc ausgeführt wird. Letzteres bietet Unterstützung für Fälschungsschutz-Token.
In Kommentaren äußern dbrunning und ccorrin Bedenken, dass Sie eingebaute AntiForgery-Token nur verwenden können, wenn Sie MVC-HTML-Helfer verwenden. Es ist nicht wahr. Helfer können nur sitzungsbasierte Tokenpaare verfügbar machen, die Sie gegeneinander validieren können. Siehe unten für Details.
AKTUALISIEREN:
Es gibt zwei Methoden, die Sie mit AntiForgery verwenden können:
AntiForgery.GetTokens
verwendet zwei out-Parameter, um das Cookie-Token und das Formular-Token zurückzugebenAntiForgery.Validate(cookieToken, formToken)
Überprüft, ob ein Token-Paar gültig istSie können diese beiden Methoden vollständig neu verwenden und formToken als headerToken und cookieToken als tatsächliches cookieToken verwenden. Rufen Sie dann einfach validate für beide innerhalb des Attributs auf.
Eine andere Lösung ist die Verwendung von JWT (überprüfen Sie z. B. die Implementierung von MembershipReboot ).
Dieser Link zeigt, wie Sie integrierte Anti-Fälschungs-Token mit Ajax verwenden:
<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); }
Schauen Sie sich auch diese Frage an. AngularJS kann kein XSRF-TOKEN-Cookie finden
quelle
Ich denke, Ihr Code ist fehlerhaft. Die ganze Idee, CSRF zu verhindern, besteht darin, bei jeder ANFRAGE und nicht bei jeder Sitzung ein eindeutiges Token zu verhindern. Wenn es sich bei dem Anti-Fälschungs-Token um einen Sitzungswert handelt, bleibt die Fähigkeit zur Durchführung von CSRF bestehen. Sie müssen bei jeder Anfrage ein eindeutiges Token angeben ...
quelle
Diese Lösung ist nicht sicher, da CSRF-Angriffe weiterhin möglich sind, solange das Auth-Cookie gültig ist. Sowohl das auth- als auch das xsrf-Cookie werden an den Server gesendet, wenn ein Angreifer Sie dazu bringt, eine Anforderung über eine andere Site auszuführen. Daher sind Sie weiterhin anfällig, bis der Benutzer eine "harte" Abmeldung vornimmt.
Jede Anforderung oder Sitzung sollte über ein eigenes Token verfügen, um CRSF-Angriffe wirklich zu verhindern. Die wahrscheinlich beste Lösung ist jedoch, keine Cookie-basierte Authentifizierung, sondern eine Token-basierte Authentifizierung wie OAuth zu verwenden. Dies verhindert, dass andere Websites Ihre Cookies verwenden, um unerwünschte Anforderungen auszuführen, da die Token in http-Headern anstelle von Cookies verwendet werden. Und http-Header werden nicht automatisch gesendet.
Diese hervorragenden Blog-Beiträge enthalten Informationen zur Implementierung von OAuth für WebAPI. Die Blog-Beiträge enthalten auch großartige Informationen zur Integration in AngularJS.
Eine andere Lösung könnte darin bestehen, CORS zu deaktivieren und nur eingehende Anforderungen von Domänen mit Whitelist zu akzeptieren. Dies funktioniert jedoch nicht für Anwendungen außerhalb der Website, z. B. für mobile und / oder Desktop-Clients. Darüber hinaus kann der Angreifer, sobald Ihre Website für einen XSS-Angriff anfällig ist, weiterhin Anfragen auf Ihrer Seite fälschen.
quelle
Ich hatte keine Probleme mit dem Code, daher halte ich die Frage für beantwortet.
quelle