Implementieren Sie CSRF im Winkel zu Asp.Net WebApi auf dem Server

70

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?

dbruning
quelle
1
Ich versuche auch hier eine Lösung zu finden und frage mich, ob ein Vergleich der beiden Cookies in Ordnung ist, wenn beide von einem Angreifer geändert werden können. Wenn Ihr Salz entdeckt wird, ist dies dann nicht gefährdet?
BenCr
BenCr, nur Javascript, das auf meiner Domain ausgeführt wird, kann das Cookie lesen und in den Header einfügen. Wenn es also eine böswillige Site gab, die den Browser veranlasste, eine Anfrage an meine Site zu senden, hatte die Anfrage keinen Header, sodass die Anfrage abgelehnt wurde.
Dbruning
Können Sie erklären, was das Ergebnis der hier beschriebenen Lösung ist? Wie scheitert es? oder bitten Sie uns, Lücken in der Sicherheit zu finden?
user1852503
Ich suche nur nach einem Kommentar. Es scheitert nicht (AFAIK)
dbruning
2
Für alle zukünftigen Benutzer ist dies ein hilfreicher Link für den Fall, dass Sie mit Asp.net MVC und AngularJs arbeiten
Shankbond

Antworten:

8

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

  • AntiForgery.Validate(cookieToken, formToken) Überprüft, ob ein Token-Paar gültig ist

Sie 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

vittore
quelle
11
Die Anti-Fälschungs-Unterstützung in asp.net mvc basiert auf der Verwendung von mvc zum Generieren Ihres HTML-Codes, damit das Anforderungsüberprüfungstoken als verstecktes Feld in Ihre HTML-Formulare eingefügt werden kann. Ich verwende kein MVC, daher haben meine HTML-Formulare dieses Token nicht.
Dbruning
@dbruning Es ist nur Helfer Generation Token, können Sie es verwenden , wo immer Sie wollen
vittore
1
Könnte sein. Ich erinnere mich nicht an die genauen Details, aber ich konnte keinen sauberen Weg finden, nur nach dem CSRF-Cookie zu fragen. Die integrierten AntiForgery-Methoden scheinen mit Formularen arbeiten zu wollen, während ich nur mit POST-JSON-Daten arbeite. Wenn Sie einen sauberen Weg zum Abrufen des csrf-Cookies freigeben können, könnte dies meine oben genannte CsrfTokenHelper-Klasse ersetzen. Sie benötigen immer noch eine gute Möglichkeit, das Cookie für die ausgehende Anforderung zu setzen und den Header für die eingehende Anforderung zu überprüfen.
Dbruning
6
Für Benutzer, die MVC nicht für ihre Ansichten verwenden möchten, sind die MVC-Helfer keine Option. Viele Leute möchten ihren clientseitigen Code in reinem HTML / JS halten, um die Vorteile mehrerer Plattformen zu nutzen und Tools wie Phonegap zu verwenden. Wenn Ihre Ansichten rasiermesserscharf sind, sind Sie in dieser Hinsicht eingeschränkt.
Ccorrin
3
@ccorrin bist du meinem Link gefolgt? Es gibt eine Option für Ajax-Fall, Sie können es verwenden.
Vittore
0

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

Kolchy
quelle
2
OWASP sagt, dass es Standard ist, es pro Sitzung zu tun
dbruning
0

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.

  1. Token-basierte Authentifizierung mit ASP.NET Web API 2, Owin und Identität
  2. AngularJS-Tokenauthentifizierung mit ASP.NET Web API 2, Owin und Identität

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.

Dibran
quelle
3
Das stimmt nicht Die schädliche Website kann nicht dazu führen, dass der Browser den X-XSRF-TOKEN- Header setzt .
Dbruning
So scheint Angulars CookieXSRFStrategy zu funktionieren: owasp.org/index.php/… . Ich werde diese Strategie für eine REST-API verwenden.
CM
-5

Ich hatte keine Probleme mit dem Code, daher halte ich die Frage für beantwortet.

dbruning
quelle