Was ist ein CSRF-Token? Welche Bedeutung hat es und wie funktioniert es?

628

Ich schreibe eine Anwendung (Django, das passiert auch) und möchte nur eine Vorstellung davon haben, was ein "CSRF-Token" eigentlich ist und wie es die Daten schützt. Sind die Post-Daten nicht sicher, wenn Sie keine CSRF-Token verwenden?

Shawn
quelle
13
Es ist ein geheimes, benutzerspezifisches Token in allen Formularübermittlungen und Nebenwirkungs-URLs, um Fälschungen von standortübergreifenden Anforderungen zu verhindern. Weitere Infos hier: en.wikipedia.org/wiki/Cross-site_request_forgery
Robert Harvey
1
Es scheint, als gäbe es eine feine Grenze zwischen dem Schutz einer Frage und dem Verbot, sie zu weit zu
fassen
2
Aus dem ORASP-Spickzettel zur Verhinderung von Cross-Site Request Forgery (CSRF) : " Cross-Site Scripting ist nicht erforderlich, damit CSRF funktioniert. Es kann jedoch jede Sicherheitsanfälligkeit bezüglich Cross-Site Scripting verwendet werden, um alle CSRF-Schadensbegrenzungstechniken zu beseitigen [...]. Dies liegt daran, dass eine XSS-Nutzlast einfach jede Seite auf der Site mit einem XMLHttpRequest [...] lesen kann. Es ist unbedingt erforderlich, dass keine XSS-Schwachstellen vorhanden sind, um sicherzustellen, dass CSRF-Abwehrmaßnahmen nicht umgangen werden können. "
toraritte

Antworten:

1497

Cross-Site Request Forgery (CSRF) in einfachen Worten

  • Angenommen, Sie sind derzeit bei Ihrem Online-Banking unter angemeldet www.mybank.com
  • Angenommen, eine Überweisung von mybank.comführt zu einer Anforderung (konzeptionell) des Formulars http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>. (Ihre Kontonummer wird nicht benötigt, da dies durch Ihre Anmeldung impliziert wird.)
  • Sie besuchen www.cute-cat-pictures.org, ohne zu wissen, dass es sich um eine bösartige Website handelt.
  • Wenn der Eigentümer dieser Website die Form der oben genannten Anfrage kennt (einfach!) Und richtig vermutet, dass Sie angemeldet sind mybank.com(erfordert etwas Glück!), Könnte er auf seiner Seite eine Anfrage wie http://www.mybank.com/transfer?to=123456;amount=10000(wo 123456ist die Nummer seines Cayman Islands-Kontos) einfügen und 10000ist ein Betrag, von dem Sie vorher dachten, dass Sie ihn gerne besitzen würden).
  • Sie haben diese www.cute-cat-pictures.orgSeite abgerufen , sodass Ihr Browser diese Anfrage stellt.
  • Ihre Bank kann diesen Ursprung der Anfrage nicht erkennen: Ihr Webbrowser sendet die Anfrage zusammen mit Ihrem www.mybank.comCookie und sie sieht absolut legitim aus. Da geht dein Geld!

Dies ist die Welt ohne CSRF-Token .

Nun zum Besseren mit CSRF-Token :

  • Die Übertragungsanforderung wird um ein drittes Argument erweitert : http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Dieses Token ist eine riesige, nicht zu erratende Zufallszahl, mybank.comdie auf ihrer eigenen Webseite angezeigt wird, wenn sie Ihnen bereitgestellt wird. Es ist jedes Mal anders, wenn sie jemandem eine Seite zur Verfügung stellen.
  • Der Angreifer kann das Token nicht erraten, kann Ihren Webbrowser nicht davon überzeugen, es abzugeben (wenn der Browser ordnungsgemäß funktioniert ...), und der Angreifer kann daher keine gültige Anforderung erstellen, da Anforderungen mit dem Ein falsches Token (oder kein Token) wird von abgelehnt www.mybank.com.

Ergebnis: Sie behalten Ihre 10000Geldeinheiten. Ich schlage vor, Sie spenden etwas davon an Wikipedia.

(Ihr Kilometerstand kann variieren.)

BEARBEITEN von lesenswertem Kommentar:

Es ist zu beachten, dass das Skript von www.cute-cat-pictures.orgnormalerweise www.mybank.comaufgrund der HTTP-Zugriffskontrolle keinen Zugriff auf Ihr Anti-CSRF-Token hat . Dieser Hinweis ist wichtig für einige Personen, die unangemessen einen Header Access-Control-Allow-Origin: *für jede Website-Antwort senden, ohne zu wissen, wofür er bestimmt ist, nur weil sie die API von einer anderen Website nicht verwenden können.

Lutz Prechelt
quelle
36
Und natürlich würde das Token idealerweise als Anti- CSRF-Token bezeichnet, aber der Name ist wahrscheinlich so kompliziert wie er ist.
Lutz Prechelt
3
@LutzPrechelt danke. Warum kann Javascript keine Authentizitätstoken vom Browser erhalten?
BKSpurgeon
72
Es ist zu beachten, dass das Skript von www.cute-cat-pictures.orgnormalerweise www.mybank.comaufgrund der HTTP-Zugriffskontrolle keinen Zugriff auf Ihr Anti-CSRF-Token hat . Dieser Hinweis ist wichtig für einige Personen, die unangemessen einen Header Access-Control-Allow-Origin: *für jede Website-Antwort senden, ohne zu wissen, wofür er bestimmt ist, nur weil sie die API von einer anderen Website nicht verwenden können.
SOFe
9
@AugustinRiedinger Wenn der Angreifer die Webseite auf seinem Computer öffnet - da er nicht über das Cookie des angemeldeten Benutzers verfügt - erhält er nicht das entsprechende CSRF-Token (jedes CSRF-Token sollte nur für eine bestimmte Benutzersitzung gültig sein). Wenn der Angreifer versucht, die Webseite mit dem Token mit einem Skript auf der Website von cute-cat-images auf den Computer des Benutzers zu laden, verhindert der Browser, dass er die Website www.mybank.com (und das Token) liest gleiche Ursprungspolitik.
Marcel
13
@LutzPrechelt Ich denke, es reicht nicht aus, dass das Token immer anders ist, es muss mit einer Sitzung gekoppelt werden und der Server muss überprüfen, ob das empfangene Token für eine Sitzung generiert wurde, die der Server durch das empfangene Cookie identifiziert. Andernfalls kann der Hacker einfach mybank selbst besuchen und einen gültigen Token erhalten. Wenn Sie also für jedes Formular ein neues Token verwenden, müssen Sie es zusammen mit der Sitzungs-ID auf dem Server speichern. Es ist wahrscheinlich einfacher, dasselbe Token pro Sitzung zu verwenden.
Marcel
222

Ja, die Postdaten sind sicher. Der Ursprung dieser Daten ist jedoch nicht. Auf diese Weise kann jemand Benutzer mit JS dazu verleiten, sich bei Ihrer Website anzumelden, während er die Webseite des Angreifers durchsucht.

Um dies zu verhindern, sendet django einen zufälligen Schlüssel sowohl im Cookie als auch in Formulardaten. Wenn Benutzer POSTs senden, wird überprüft, ob zwei Schlüssel identisch sind. Wenn der Benutzer betrogen wird, kann die Website eines Drittanbieters die Cookies Ihrer Website nicht abrufen, was zu einem Authentifizierungsfehler führt.

Dmitry Shevchenko
quelle
@DmitryShevchenko Hallo, versuchen Sie zu verstehen, wie sich diese Methode der Cookie + Formulareingabe von der Validierung des Referrers auf der Serverseite unterscheidet? Alle Beispiele, die ich finde, beziehen sich auf einen Hacker, der den Benutzer dazu verleitet, von seiner Site auf die eigentliche Site zu posten.
Ethan
Ok, ich habe herausgefunden, warum der Referrer nicht verwendet wird. In vielen Fällen ist es blockiert, da manchmal vertrauliche Informationen gespeichert werden. Unternehmen und ihre Stimmrechtsvertreter tun dies normalerweise. Wenn jedoch HTTPS verwendet wird, ist die Wahrscheinlichkeit höher, dass es nicht blockiert wird.
Ethan
4
Es ist einfach, den Referrer zu ändern. Ich würde nicht sagen, dass es sich um eine zuverlässige Information handelt. CSRF-Token wird jedoch unter Verwendung des geheimen Serverschlüssels generiert und normalerweise an den Benutzer gebunden
Dmitry Shevchenko
1
Ich verstehe nicht wirklich, warum dies eine Sicherheitsbedrohung ist. Der Benutzer wird auf einer anderen Site angemeldet ... aber die ursprüngliche Site kann diese Informationen nicht abrufen. Recht?
Aakil Fernandes
6
Angenommen, ich füge einen böswilligen Iframe von " bank.com/transfer?from=x&to=y " beispielsweise in Facebook.com ein. Wenn Sie Kunde von bank.com sind und zu Facebook gehen, lädt dieser Iframe die Bankseite mit Ihren Cookies (weil der Browser sie an eine bekannte Domain weiterleitet) und führt eine Geldüberweisung durch. Ohne dass du etwas weißt.
Dmitry Shevchenko
74

Die Site generiert ein eindeutiges Token, wenn sie die Formularseite erstellt. Dieses Token ist erforderlich, um Daten an den Server zurückzusenden.

Da das Token von Ihrer Site generiert und nur bereitgestellt wird, wenn die Seite mit dem Formular generiert wird, kann eine andere Site Ihre Formulare nicht nachahmen - sie haben das Token nicht und können daher nicht auf Ihrer Site posten.

tkone
quelle
10
Könnte ein Benutzer die Token-Ausgabe in der Quelle abrufen, das an ihn gesendete Cookie abrufen und dann von einer Website eines Drittanbieters senden?
Jack Marchetti
9
@ JackMarchetti ja. Dies wäre jedoch kostspielig, da Sie jedes Mal, wenn Sie das Formular von einer Website eines Drittanbieters senden möchten, die Seite laden und das Token analysieren müssen. CSRF-Token sollten idealerweise mit anderen Sicherheitsformen gekoppelt werden, wenn Sie sich mit diesem Angriffsvektor
befassen
4
Ich habe die gleiche Frage wie @JackMarchetti. Was nicht klar ist, ist, ob sich das CSRF-Token bei jedem Login ändert. Wenn dies gleich bleibt, was würde einen Angreifer daran hindern, sich zuerst anzumelden, das Anforderungstoken zu greifen und dieses Token dann in den Angriff einzufügen?
Paul Preibisch
7
@PaulPreibisch sollte sich bei jedem Laden der Seite ändern - nicht bei jedem Login. Auf diese Weise müsste der Angreifer die Seite jedes Mal anfordern, wenn er das Formular senden möchte. Macht es viel schwieriger.
Tkone
9
@tkone, es macht es nicht wirklich viel schwieriger. Wenn nur der Aufwand und die Zeit verdoppelt. Es wird keine unzulässige Verarbeitung hinzugefügt. Der Trick besteht auch darin, das CSRF-Token einem domänenspezifischen Cookie zuzuordnen und dieses Cookie zusammen mit dem Formular zu senden. Sowohl das Cookie als auch die Formularpostdaten müssten bei der POST-Anforderung an den Server gesendet werden. Auf diese Weise würde ein Cookie-Hijacking-Angriff erforderlich sein, um eine legitime Anfrage emulieren zu können.
Pedro Cordeiro
55

Der Cloud Under-Blog bietet eine gute Erklärung für CSRF-Token.

Stellen Sie sich vor, Sie hätten eine Website wie ein vereinfachtes Twitter, die auf a.com gehostet wird. Angemeldete Benutzer können Text (einen Tweet) in ein Formular eingeben, das als POST-Anfrage an den Server gesendet und veröffentlicht wird, wenn sie auf die Schaltfläche "Senden" klicken. Auf dem Server wird der Benutzer durch ein Cookie identifiziert, das seine eindeutige Sitzungs-ID enthält, sodass Ihr Server weiß, wer den Tweet gepostet hat.

Das Formular könnte so einfach sein:

 <form action="http://a.com/tweet" method="POST">
   <input type="text" name="tweet">
   <input type="submit">
 </form> 

Stellen Sie sich nun vor, ein Bösewicht kopiert dieses Formular und fügt es in seine bösartige Website ein, sagen wir b.com. Das Formular würde immer noch funktionieren. Solange ein Benutzer bei Ihrem Twitter angemeldet ist (dh er hat ein gültiges Sitzungscookie für a.com), wird die POST-Anforderung http://a.com/tweetwie gewohnt an gesendet und verarbeitet, wenn der Benutzer auf die Schaltfläche " Senden" klickt.

Bisher ist dies kein großes Problem, solange der Benutzer darüber informiert wird, was das Formular genau tut, aber was ist, wenn unser Bösewicht das Formular so ändert:

 <form action="https://example.com/tweet" method="POST">
   <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad">
   <input type="submit" value="Click to win!">
 </form> 

Wenn nun einer Ihrer Benutzer auf der Website des Bösen landet und auf "Klicken, um zu gewinnen!" Klicken Sie auf die Schaltfläche, das Formular wird an Ihre Website gesendet, der Benutzer wird anhand der Sitzungs-ID im Cookie korrekt identifiziert und der versteckte Tweet wird veröffentlicht.

Wenn unser Bösewicht noch schlimmer wäre, würde er den unschuldigen Benutzer dazu bringen, dieses Formular einzureichen, sobald er seine Webseite mit JavaScript öffnet, vielleicht sogar vollständig versteckt in einem unsichtbaren Iframe. Dies ist im Grunde eine standortübergreifende Fälschung von Anfragen.

Ein Formular kann einfach von überall nach überall eingereicht werden. Im Allgemeinen ist dies eine häufige Funktion, aber es gibt noch viele weitere Fälle, in denen es wichtig ist, nur das Senden eines Formulars von der Domäne zuzulassen, zu der es gehört.

Noch schlimmer ist es, wenn Ihre Webanwendung nicht zwischen POST- und GET-Anforderungen unterscheidet (z. B. in PHP, indem Sie $ _REQUEST anstelle von $ _POST verwenden). Tu das nicht! Anfragen zur Änderung von Daten können so einfach wie <img src="http://a.com/tweet?tweet=This+is+really+bad">eingebettet in eine schädliche Website oder sogar in eine E-Mail gesendet werden.

Wie stelle ich sicher, dass ein Formular nur von meiner eigenen Website eingereicht werden kann? Hier kommt das CSRF-Token ins Spiel. Ein CSRF-Token ist eine zufällige, schwer zu erratende Zeichenfolge. Auf einer Seite mit einem Formular, das Sie schützen möchten, generiert der Server eine zufällige Zeichenfolge, das CSRF-Token, fügt es dem Formular als verstecktes Feld hinzu und merkt es sich auch irgendwie, indem er es in der Sitzung speichert oder ein Cookie setzt mit dem Wert. Jetzt würde das Formular folgendermaßen aussehen:

    <form action="https://example.com/tweet" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="tweet">
      <input type="submit">
    </form> 

Wenn der Benutzer das Formular sendet, muss der Server lediglich den Wert des veröffentlichten Felds csrf-token (der Name spielt keine Rolle) mit dem CSRF-Token vergleichen, an das sich der Server erinnert. Wenn beide Zeichenfolgen gleich sind, kann der Server das Formular weiter verarbeiten. Andernfalls sollte der Server die Verarbeitung des Formulars sofort beenden und mit einem Fehler antworten.

Warum funktioniert das? Es gibt mehrere Gründe, warum der Bösewicht aus unserem obigen Beispiel den CSRF-Token nicht erhalten kann:

Das Kopieren des statischen Quellcodes von unserer Seite auf eine andere Website wäre nutzlos, da sich der Wert des ausgeblendeten Felds mit jedem Benutzer ändert. Ohne dass die Website des Bösewichts das CSRF-Token des aktuellen Benutzers kennt, würde Ihr Server die POST-Anforderung immer ablehnen.

Da die bösartige Seite des Bösewichts vom Browser Ihres Benutzers aus einer anderen Domain (b.com anstelle von a.com) geladen wird, hat der Bösewicht keine Chance, ein JavaScript zu codieren, das den Inhalt und damit das aktuelle CSRF-Token unseres Benutzers lädt deine Website. Dies liegt daran, dass Webbrowser standardmäßig keine domänenübergreifenden AJAX-Anforderungen zulassen.

Der Bösewicht kann auch nicht auf das von Ihrem Server gesetzte Cookie zugreifen, da die Domänen nicht übereinstimmen.

Wann sollte ich mich vor Fälschungen von standortübergreifenden Anfragen schützen? Wenn Sie sicherstellen können, dass Sie GET, POST und andere Anforderungsmethoden nicht wie oben beschrieben verwechseln, ist es ein guter Anfang, standardmäßig alle POST-Anforderungen zu schützen.

Sie müssen PUT- und DELETE-Anforderungen nicht schützen, da, wie oben erläutert, ein Standard-HTML-Formular von einem Browser mit diesen Methoden nicht gesendet werden kann.

JavaScript hingegen kann zwar andere Arten von Anforderungen stellen, z. B. mithilfe der Funktion $ .ajax () von jQuery. Beachten Sie jedoch, dass die Domänen übereinstimmen müssen, damit AJAX-Anforderungen funktionieren (sofern Sie Ihren Webserver nicht explizit anders konfigurieren). .

Dies bedeutet, dass Sie AJAX-Anforderungen häufig nicht einmal ein CSRF-Token hinzufügen müssen, selbst wenn es sich um POST-Anforderungen handelt. Sie müssen jedoch sicherstellen, dass Sie die CSRF-Prüfung in Ihrer Webanwendung nur umgehen, wenn es sich bei der POST-Anforderung tatsächlich um eine handelt AJAX-Anfrage. Sie können dies tun, indem Sie nach einem Header wie X-Requested-With suchen, den AJAX-Anforderungen normalerweise enthalten. Sie können auch einen anderen benutzerdefinierten Header festlegen und dessen Vorhandensein auf der Serverseite überprüfen. Das ist sicher, da ein Browser einer regulären HTML-Formularübermittlung keine benutzerdefinierten Header hinzufügen würde (siehe oben), sodass Mr Bad Guy dieses Verhalten nicht mit einem Formular simulieren kann.

Wenn Sie Zweifel an AJAX-Anforderungen haben, weil Sie aus irgendeinem Grund nicht nach einem Header wie X-Requested-With suchen können, übergeben Sie das generierte CSRF-Token einfach an Ihr JavaScript und fügen Sie das Token der AJAX-Anforderung hinzu. Es gibt verschiedene Möglichkeiten, dies zu tun. Fügen Sie es entweder wie ein normales HTML-Formular zur Nutzlast hinzu oder fügen Sie der AJAX-Anforderung einen benutzerdefinierten Header hinzu. Solange Ihr Server weiß, wo er in einer eingehenden Anforderung danach suchen muss, und in der Lage ist, ihn mit dem ursprünglichen Wert zu vergleichen, an den er sich aus der Sitzung oder dem Cookie erinnert, sind Sie sortiert.

Dan
quelle
Danke für die detaillierten Infos. Während der Post-Anfrage muss die Site das CSRF-Token an den Server senden. Wann sendet der Client dieses CSRF-Token an den Server? Ist es während der Anforderung der Preflight-Optionen? Bitte elablorate auf diesem Teil ..
Sm Srikanth
@Dan Wie kommt es, dass b.com auf die Cookies einer anderen Website a.com zugreifen kann?
Zakir
8

Der Grund dafür ist, sicherzustellen, dass die Anforderungen von den tatsächlichen Benutzern der Site stammen. Für die Formulare wird ein CSRF-Token generiert, das an die Sitzungen des Benutzers gebunden sein muss. Es wird verwendet, um Anforderungen an den Server zu senden, auf dem das Token sie überprüft. Dies ist eine Möglichkeit zum Schutz vor CSRF, eine andere wäre die Überprüfung des Referrer-Headers.

gerne
quelle
7
Verlassen Sie sich nicht auf den Referer-Header, er kann leicht gefälscht werden.
Kag
3
Das ist die richtige Antwort! Das Token MUSS an eine Sitzung auf dem Server gebunden sein. Der Vergleich von Cookie + Formulardaten wie die am häufigsten gewählte Antwort legt nahe, dass dies völlig falsch ist. Diese Komponenten sind beide Teil der Anforderung, die der Client erstellt.
Lee Davis
3
Nicht wirklich. Das Token MUSS an jede ANFRAGE an den Server gebunden sein. Wenn Sie es nur an die Sitzung binden, besteht die Gefahr, dass jemand das Token der Sitzung stiehlt und eine Anforderung mit diesem Token sendet. Für maximale Sicherheit muss das Token an jede http-Anforderung gebunden sein.
chrisl08