Ich versuche, das gesamte Problem mit CSRF zu verstehen und geeignete Wege zu finden, um es zu verhindern. (Ressourcen, die ich gelesen habe, verstehe und stimme zu: OWASP CSRF Prevention-Informationsblatt , Fragen zu CSRF .)
Soweit ich es verstehe, wird die Sicherheitsanfälligkeit in Bezug auf CSRF durch die Annahme eingeführt, dass (aus Sicht des Webservers) ein gültiger Sitzungscookie in einer eingehenden HTTP-Anforderung die Wünsche eines authentifizierten Benutzers widerspiegelt. Alle Cookies für die Origin-Domäne werden jedoch vom Browser auf magische Weise an die Anfrage angehängt, sodass wirklich alle Server aus dem Vorhandensein eines gültigen Sitzungscookies in einer Anfrage schließen können, dass die Anfrage von einem Browser kommt, der eine authentifizierte Sitzung hat. Es kann nicht weiter davon ausgegangen werden, dass code in diesem Browser ausgeführt wird oder ob es wirklich die Wünsche der Benutzer widerspiegelt. Um dies zu verhindern, können Sie zusätzliche Authentifizierungsinformationen (das "CSRF-Token") in die Anforderung aufnehmen, die auf andere Weise als durch die automatische Cookie-Verarbeitung des Browsers übermittelt werden. Kurz gesagt, das Sitzungs-Cookie authentifiziert den Benutzer/Browser und das CSRF-Token authentifiziert den Code, der im Browser ausgeführt wird.
Wenn Sie also ein Session-Cookie zur Authentifizierung von Benutzern Ihrer Webanwendung verwenden, sollten Sie jeder Antwort ein CSRF-Token hinzufügen. In jeder (mutierenden) Anforderung ist ein entsprechendes CSRF-Token erforderlich. Das CSRF-Token macht dann einen Roundtrip von Server zu Browser zurück zum Server, um dem Server zu zeigen, dass die Seite, von der die Anforderung stammt, von diesem Server genehmigt wird (von diesem generiert wird).
Weiter zu meiner Frage, die sich auf die spezifische Transportmethode bezieht, die für dieses CSRF-Token auf dieser Rundreise verwendet wird.
Es scheint üblich (z. B. in AngularJS , Django , Rails ), das CSRF - Token vom Server an den Client als Cookie (dh in einem Set - Cookie - Header) zu senden, und anschließend Javascript zu verwenden Im Client kratzen Sie es aus dem Cookie und hängen es als separaten XSRF-TOKEN-Header an, um es an den Server zu senden.
(Eine alternative Methode ist die von z. B. Express empfohlene Methode, bei der das vom Server generierte CSRF - Token über eine serverseitige Vorlagenerweiterung in den Antworttext eingeschlossen wird und direkt an den Code/Markup angehängt wird, an den es zurückgegeben wird B. als verborgene Formulareingabe. Dieses Beispiel ist eine mehr Web 1.0-artige Vorgehensweise, würde aber gut auf einen JS-schweren Client verallgemeinern.)
Warum ist es so üblich, Set-Cookie als nachgelagerten Transport für das CSRF-Token zu verwenden/warum ist dies eine gute Idee? Ich kann mir vorstellen, dass die Autoren all dieser Frameworks ihre Optionen sorgfältig geprüft und das nicht falsch verstanden haben. Auf den ersten Blick scheint die Verwendung von Cookies zur Umgehung dessen, was im Wesentlichen eine Einschränkung beim Design von Cookies ist, dumm. Wenn Sie Cookies als Roundtrip-Transport verwenden (Set-Cookie: Header Downstream für den Server, um dem Browser das CSRF-Token mitzuteilen, und Cookie: Header Upstream, damit der Browser den Server an den Server zurücksenden kann), würden Sie die Sicherheitsanfälligkeit wieder herstellen versuchen zu beheben.
Mir ist klar, dass die oben genannten Frameworks keine Cookies für den gesamten Roundtrip für das CSRF-Token verwenden. Sie verwenden Set-Cookie Downstream, dann etwas anderes (z. B. einen X-CSRF-Token-Header), und dies schließt die Sicherheitsanfälligkeit aus. Aber auch die Verwendung von Set-Cookie als nachgelagerten Transport ist potenziell irreführend und gefährlich. Der Browser hängt nun das CSRF-Token an jede Anforderung an, einschließlich böswilliger XSRF-Anforderungen. Im besten Fall wird die Anforderung dadurch größer, als sie sein muss, und im schlimmsten Fall könnte ein gut gemeinter, aber fehlgeleiteter Servercode tatsächlich versuchen, sie zu verwenden, was wirklich schlecht wäre. Da der eigentliche vorgesehene Empfänger des CSRF-Token clientseitiges Javascript ist, bedeutet dies, dass dieses Cookie nicht mit nur http geschützt werden kann. Das Senden des CSRF-Tokens in einem Set-Cookie-Header stromabwärts erscheint mir daher eher suboptimal.
Ein guter Grund, auf den Sie angesprochen haben, ist, dass das CSRF-Cookie nach Erhalt für die gesamte Anwendung im Clientskript zur Verwendung in regulären Formularen und in AJAX POSTs verfügbar ist. Dies ist in einer JavaScript-starken Anwendung wie der von AngularJS sinnvoll (für die Verwendung von AngularJS ist es nicht erforderlich, dass die Anwendung eine Anwendung mit einer einzelnen Seite ist. Daher ist es hilfreich, wenn der Status zwischen verschiedenen Seitenanforderungen mit dem CSRF-Wert wechseln muss kann normalerweise nicht im Browser verbleiben).
Berücksichtigen Sie die folgenden Szenarien und Prozesse in einer typischen Anwendung für einige Vor- und Nachteile der von Ihnen beschriebenen Ansätze. Diese basieren auf dem Synchronizer Token Pattern .
Vorteile:
Nachteile:
Vorteile:
Nachteile:
Vorteile:
Nachteile:
Vorteile:
Nachteile:
Vorteile:
Nachteile:
Daher ist der Cookie-Ansatz ziemlich dynamisch und bietet eine einfache Möglichkeit, den Cookie-Wert (jede HTTP-Anfrage) abzurufen und zu verwenden (JS kann den Wert automatisch zu jedem Formular hinzufügen und er kann auch in AJAX-Anfragen verwendet werden) als Überschrift oder als Formularwert). Sobald das CSRF-Token für die Sitzung empfangen wurde, muss es nicht erneut generiert werden, da ein Angreifer, der einen CSRF-Exploit verwendet, keine Methode zum Abrufen dieses Tokens hat. Wenn ein böswilliger Benutzer versucht, das CSRF-Token des Benutzers mit einer der oben genannten Methoden zu lesen, wird dies durch die Same Origin Policy verhindert. Wenn ein böswilliger Benutzer versucht, die CSRF-Tokenserverseite abzurufen (z. B. über curl
), wird dieses Token nicht demselben Benutzerkonto zugeordnet, mit dem das Authentifizierungssitzungscookie des Opfers in der Anforderung fehlt (dies wäre das Angreifer - daher wird es der Sitzung des Opfers nicht serverseitig zugeordnet).
Neben dem Synchronizer Token Pattern gibt es auch die Double Submit Cookie CSRF Prevention-Methode, die natürlich Cookies verwendet, um eine Art CSRF-Token zu speichern. Dies ist einfacher zu implementieren, da für das CSRF-Token kein serverseitiger Status erforderlich ist. Tatsächlich könnte das CSRF-Token das Standardauthentifizierungscookie sein, wenn diese Methode verwendet wird, und dieser Wert wird wie bei der Anforderung üblich über Cookies übermittelt. Der Wert wird jedoch auch in einem ausgeblendeten Feld oder in einem Header wiederholt, unter dem ein Angreifer nicht replizieren kann Sie können den Wert überhaupt nicht lesen. Es wird jedoch empfohlen, ein anderes Cookie als das Authentifizierungscookie zu wählen, damit das Authentifizierungscookie durch Markieren von HttpOnly gesichert werden kann. Dies ist also ein weiterer häufiger Grund, warum Sie CSRF-Prävention mithilfe einer Cookie-basierten Methode finden.
Die Verwendung eines Cookies zum Bereitstellen des CSRF-Token für den Client lässt keinen erfolgreichen Angriff zu, da der Angreifer den Wert des Cookies nicht lesen kann und ihn daher nicht an die Stelle setzen kann, an der die serverseitige CSRF-Überprüfung dies erfordert.
Der Angreifer kann eine Anforderung an den Server senden, wobei sowohl das Authentifizierungs-Token-Cookie als auch das CSRF-Cookie in den Anforderungsheadern enthalten ist. Der Server sucht jedoch nicht nach dem CSRF-Token als Cookie in den Anforderungsheadern. Er sucht in der Nutzlast der Anforderung. Und selbst wenn der Angreifer weiß, wo er den CSRF-Token in der Nutzlast ablegen muss, muss er seinen Wert lesen, um ihn dort abzulegen. Die Cross-Origin-Richtlinie des Browsers verhindert jedoch das Lesen von Cookie-Werten von der Ziel-Website.
Dieselbe Logik gilt nicht für das Auth-Token-Cookie, da der Server dies in den Request-Headern erwartet und der Angreifer keine besonderen Vorkehrungen treffen muss, um ihn dort abzulegen.
Meine beste Vermutung zur Antwort: Betrachten Sie diese 3 Optionen, um das CSRF-Token vom Server zum Browser zu übertragen.
Ich denke, der erste Antrag, Anforderungshauptteil (obwohl von dem Express-Tutorial, das ich in der Frage verlinkt habe ), ist nicht so tragbar für eine Vielzahl von Situationen; Nicht jeder generiert jede HTTP-Antwort dynamisch. Wenn Sie das Token letztendlich in die generierte Antwort einfügen müssen, kann dies sehr unterschiedlich sein (in einer verborgenen Form, in einem Fragment von JS-Code oder in einer Variablen, auf die anderer JS-Code zugreifen kann) CSRF-Token setzen). Obwohl # 1 mit einigen Anpassungen durchführbar ist, ist # 1 ein schwieriger Ort, um eine Einheitslösung zu finden.
Der zweite, der benutzerdefinierte Header, ist attraktiv, funktioniert aber nicht, da JS kann zwar die Header für ein XHR aufrufen, das es aufgerufen hat, aber es kann keine Header für die geladene Seite erhalten .
Der dritte, ein Cookie, der von einem Set-Cookie-Header getragen wird, bleibt in allen Situationen einfach zu verwenden (jeder Server kann Cookie-Header pro Anfrage setzen, und es spielt keine Rolle, welche Art von Cookie es ist) Daten befinden sich im Anforderungshauptteil). Trotz seiner Nachteile war dies die einfachste Methode für Frameworks, die breit implementiert werden konnte.
Abgesehen von dem Sitzungs-Cookie (der eine Art Standard ist), möchte ich keine zusätzlichen Cookies verwenden.
Ich habe eine Lösung gefunden, die für mich beim Erstellen einer Single Page Web Application (SPA) mit vielen AJAX -Anfragen funktioniert. Hinweis: Ich verwende serverseitiges Java und clientseitiges JQuery, aber keine magischen Dinge. Ich denke, dieses Prinzip kann in allen gängigen Programmiersprachen implementiert werden.
Meine Lösung ohne zusätzliche Cookies ist einfach:
Speichern Sie das CSRF-Token, das nach einer erfolgreichen Anmeldung vom Server zurückgegeben wird, in einer globalen Variablen (wenn Sie Web-Speicher statt eines globalen Speichers verwenden möchten, der natürlich gut ist). Weisen Sie JQuery an, in jedem AJAX - Aufruf einen X-CSRF-TOKEN-Header anzugeben.
Die Hauptseite "Index" enthält dieses JavaScript-Snippet:
// Intialize global variable CSRF_TOKEN to empty sting.
// This variable is set after a succesful login
window.CSRF_TOKEN = '';
// the supplied callback to .ajaxSend() is called before an Ajax request is sent
$( document ).ajaxSend( function( event, jqXHR ) {
jqXHR.setRequestHeader('X-CSRF-TOKEN', window.CSRF_TOKEN);
});
Erstellen Sie bei erfolgreicher Anmeldung ein zufälliges (und lang genug) CSRF-Token, speichern Sie es in der serverseitigen Sitzung und geben Sie es an den Client zurück. Filtern Sie bestimmte (vertrauliche) eingehende Anforderungen, indem Sie den X-CSRF-TOKEN-Header-Wert mit dem in der Sitzung gespeicherten Wert vergleichen. Diese sollten übereinstimmen.
Empfindliche AJAX - Aufrufe (POST-Formulardaten und GET-JSON-Daten) und der serverseitige Filter, der sie abfängt, befinden sich unter einem Pfad/dataservice/*. Anmeldeanforderungen dürfen den Filter nicht treffen, daher befinden sich diese auf einem anderen Pfad. Anforderungen für HTML-, CSS-, JS- und Bildressourcen befinden sich ebenfalls nicht im Pfad/dataservice/* und werden daher nicht gefiltert. Diese enthalten nichts Geheimnisvolles und können nichts schaden, daher ist dies in Ordnung.
@WebFilter(urlPatterns = {"/dataservice/*"})
...
String sessionCSRFToken = req.getSession().getAttribute("CSRFToken") != null ? (String) req.getSession().getAttribute("CSRFToken") : null;
if (sessionCSRFToken == null || req.getHeader("X-CSRF-TOKEN") == null || !req.getHeader("X-CSRF-TOKEN").equals(sessionCSRFToken)) {
resp.sendError(401);
} else
chain.doFilter(request, response);
}