Wie kann CSRF in einer RESTful-Anwendung verhindert werden?

78

Cross Site Request Forgery (CSRF) wird normalerweise mit einer der folgenden Methoden verhindert:

  • Referer prüfen - RESTful aber unzuverlässig
  • Token in Formular einfügen und Token in der Serversitzung speichern - nicht wirklich RESTful
  • kryptische einmalige URIs - aus dem gleichen Grund wie Token nicht RESTful
  • Kennwort manuell für diese Anforderung senden (nicht das zwischengespeicherte Kennwort, das bei der HTTP-Authentifizierung verwendet wird) - RESTful, aber nicht praktisch

Meine Idee ist es, ein Benutzergeheimnis, eine kryptische, aber statische Formular-ID und JavaScript zu verwenden, um Token zu generieren.

<form method="POST" action="/someresource" id="7099879082361234103">
    <input type="hidden" name="token" value="generateToken(...)">
    ...
</form>
  1. GET /usersecret/john_doe vom JavaScript vom authentifizierten Benutzer abgerufen.
  2. Antwort: OK 89070135420357234586534346Dieses Geheimnis ist konzeptionell statisch, kann aber jeden Tag / jede Stunde geändert werden, um die Sicherheit zu verbessern. Dies ist die einzige vertrauliche Sache.
  3. Lesen Sie die kryptische (aber für alle Benutzer statische!) Formular-ID mit JavaScript und verarbeiten Sie sie zusammen mit dem Benutzergeheimnis: generateToken(7099879082361234103, 89070135420357234586534346)
  4. Senden Sie das Formular zusammen mit dem generierten Token an den Server.
  5. Da der Server das Benutzergeheimnis und die Formular-ID kennt, kann vor dem Senden und Vergleichen der beiden Ergebnisse dieselbe generateToken-Funktion wie beim Client ausgeführt werden. Nur wenn beide Werte gleich sind, wird die Aktion autorisiert.

Stimmt etwas mit diesem Ansatz nicht, obwohl er ohne JavaScript nicht funktioniert?

Nachtrag:

Deamon
quelle
Ihr Benutzergeheimnis ist nicht für den Benutzer eindeutig. Ein Angreifer muss lediglich diese Nummer abrufen und seine Skripte anpassen, um die neue Berechnung zu verwenden. Wie authentifizieren Sie Benutzer, wenn Sie überhaupt keinen Status haben?
Mike
Das Benutzergeheimnis ist pro Benutzer eindeutig und kann nur nach der Authentifizierung (HTTP-Basis- oder Digest-Authentifizierung oder Zertifikatauthentifizierung)
abgerufen werden

Antworten:

28

Hier gibt es viele Antworten und Probleme mit einigen von ihnen.

Dinge, die Sie NICHT tun sollten:

  1. Wenn Sie das Sitzungstoken aus JavaScript lesen müssen, machen Sie etwas schrecklich Falsches. Auf Ihrem Sitzungskennungs-Cookie sollte IMMER HTTPOnly festgelegt sein, damit es für Skripte nicht verfügbar ist.

    Durch diesen einen Schutz wird die Auswirkung von XSS erheblich verringert, da ein Angreifer kein angemeldetes Sitzungstoken für Benutzer mehr erhalten kann, das in jeder Hinsicht den Anmeldeinformationen in der Anwendung entspricht. Sie möchten nicht, dass ein Fehler dem Königreich Schlüssel gibt.

  2. Die Sitzungskennung sollte nicht in den Inhalt der Seite geschrieben werden. Dies ist aus den gleichen Gründen, aus denen Sie HTTPOnly festlegen. Dies bedeutet, dass Ihr CSRF-Token nicht Ihre Sitzungs-ID sein kann. Sie müssen unterschiedliche Werte sein.

Dinge, die Sie tun sollten:

  1. Befolgen Sie die Anweisungen von OWASP :

  2. Wenn es sich um eine REST-Anwendung handelt, können Sie die doppelte Übermittlung von CSRF-Token verlangen . Wenn Sie dies tun, stellen Sie einfach sicher, dass Sie es für eine bestimmte vollständige Domain ( www.mydomain.com ) und nicht für eine übergeordnete Domain (example.com) definieren und dass Sie auch das Cookie-Attribut "samesite" verwenden, das an Bedeutung gewinnt Popularität.

Erstellen Sie einfach etwas kryptografisch Zufälliges, speichern Sie es in ASCII Hex- oder Base64-Codierung und fügen Sie es als Cookie und zu Ihren Formularen hinzu, wenn der Server die Seite zurückgibt. Stellen Sie auf der Serverseite sicher, dass der Cookie-Wert mit dem Formularwert übereinstimmt. Voila, Sie haben CSRF getötet, zusätzliche Eingabeaufforderungen für Ihre Benutzer vermieden und sich nicht für weitere Sicherheitslücken geöffnet.

HINWEIS: Wie in @krubo unten angegeben, weist die Double-Submission-Technik einige Schwachstellen auf (siehe Double-Submission) . Da diese Schwäche Folgendes erfordert:

  1. Sie definieren ein Cookie für die übergeordnete Domäne.
  2. Sie können HSTS nicht einstellen .
  3. Der Angreifer kontrolliert einen Netzwerkstandort zwischen dem Benutzer und dem Server

Ich denke, die Schwäche fällt eher in die Kategorie eines "Cool Defcon Talk" als in ein "Realworld Security Risk". In jedem Fall schadet es nicht, ein paar zusätzliche Schritte zu unternehmen, um sich vollständig zu schützen, wenn Sie die doppelte Einreichung verwenden.


Neues Update 07/06/2020

Meine neue Lieblingsmethode für die doppelte Übermittlung besteht darin, wie zuvor eine kryptografische Zufallszeichenfolge im Hauptteil der Anforderung zu erstellen und zu übergeben. Anstatt dass das Cookie den gleichen exakten Wert hat, muss das Cookie der codierte Wert der Zeichenfolge sein, die von einem Zertifikat signiert wird. Dies ist auf der Serverseite immer noch genauso einfach zu überprüfen, für einen Angreifer jedoch VIEL schwerer nachzuahmen. Sie sollten weiterhin das gleiche Cookie-Attribut und andere Schutzmaßnahmen verwenden, die weiter oben in meinem Beitrag beschrieben wurden.

Doug
quelle
XSS mit einem Sitzungscookie ist genauso anfällig wie ein XSS mit einem Token, das aus JavaScript gelesen werden kann. Wenn noch eine AJAX-Anfrage erstellt werden kann, die Geld vom Benutzerkonto auf mein Konto überweist, akzeptiert der Server dies gerne.
Ghayes
4
@ Ghays Ich bin anderer Meinung. Ihr Sitzungstoken ist viel empfindlicher als Ihr CSRF-Token. Mit Ihrem Sitzungstoken kann ich wie Sie von meinem Computer aus vollständig auf die Anwendung zugreifen. Mit dem CSRF-Token kann ich möglicherweise eine Liste von vorgefertigten vertraulichen Aktionen haben, die in Ihrem Browser ausgeführt werden. Das zweite Szenario ist viel schwieriger durchzuführen, erfordert Kenntnisse der App, benötigt ein größeres Zeitfenster für die Ausführung und die Aktionen beschränken sich auf das, was Sie zuvor geplant haben. Das erste Szenario benötigt eine Codezeile für jede Website und eine Cookie Manager-App, die der Angreifer auf seinem Computer verwenden kann.
Doug
Es ist erwähnenswert hier. Durch strenge cross-origin HTTP requestÜberprüfungen des Servers und der HTTP-Rückgabeheader von der API kann ein Großteil des automatisierten Schadens begrenzt werden, den ein Angreifer einem angemeldeten Benutzer zufügen kann.
LessQuesar
1
In den aktualisierten OWASP-Leitlinien wird die doppelte Einreichung von CSRF-Token nicht mehr als primäre Verteidigung akzeptiert, sondern in die Tiefenverteidigung verschoben. Das Problem ist, dass es besiegt werden kann, wenn der Angreifer ein Cookie schreiben kann, was er beispielsweise tun kann, wenn er eine andere Subdomain steuert.
Krubo
1
Die OWASP-Links geben jetzt 404
ParkerD
10

Verstehe ich das richtig:

  • Sie möchten Schutz vor CSRF für Benutzer, die über Cookies angemeldet sind.
  • Gleichzeitig möchten Sie eine RESTful-Schnittstelle für authentifizierte Basic-, OAuth- und Digest-Anforderungen von Apps.

Überprüfen Sie also, ob Benutzer per Cookie angemeldet sind, und wenden Sie CSRF erst dann an .

Ich bin nicht sicher, aber kann eine andere Site Dinge wie Basic Auth oder Header fälschen?

Soweit ich weiß, dreht sich bei CSRF alles um Cookies ? RESTful Auth tritt bei Cookies nicht auf.

antitoxisch
quelle
Ich habe mich auch darüber gewundert! Gemäß diesem Artikel mathieu.fenniak.net/… sollte es möglich sein, CSRF-Überprüfungen einzuschalten , wenn jemand über ein Cookie / eine Sitzung kommt, und es auszuschalten, wenn die Anforderung über ein zustandsloses Authentifizierungsschema Basic erfolgt. usw.
CMCDragonkai
4
Seien Sie vorsichtig mit der Standardauthentifizierung - sie entspricht praktisch einem Benutzer, der über ein Cookie angemeldet ist, da Browser den bereitgestellten Autorisierungsheader bei nachfolgenden Anforderungen als Benutzerfreundlichkeit senden.
Simon Lieschke
@ SimonLieschke as wird Windows / ntlm / kerberos integrieren. Wenn gesetzt, erhält der Broweser nur einen Token vom DC.
stuartm9999
9

Sie benötigen definitiv einen Status auf dem Server, um sich zu authentifizieren / zu autorisieren. Es muss jedoch nicht die http-Sitzung sein, sondern Sie können sie in einem verteilten Cache (wie memcached) oder einer Datenbank speichern.

Wenn Sie Cookies zur Authentifizierung verwenden, besteht die einfachste Lösung darin, den Cookie-Wert doppelt zu übermitteln. Bevor Sie das Formular senden, lesen Sie die Sitzungs-ID aus dem Cookie, speichern Sie sie in einem versteckten Feld und senden Sie sie dann ab. Vergewissern Sie sich auf der Serverseite, dass der Wert in der Anforderung mit der Sitzungs-ID übereinstimmt (die Sie vom Cookie erhalten haben). Böses Skript aus einer anderen Domäne kann die Sitzungs-ID nicht aus dem Cookie lesen, wodurch CSRF verhindert wird.

Dieses Schema verwendet eine einzelne Kennung in der gesamten Sitzung.

Wenn Sie mehr Schutz wünschen, generieren Sie eine eindeutige ID pro Sitzung und Formular.

Generieren Sie außerdem KEINE Token in JS. Jeder kann den Code kopieren und von einer anderen Domain ausführen, um Ihre Site anzugreifen.

Sripathi Krishnan
quelle
1
Für die Authentifizierung sind keine Sitzungen erforderlich, wie die HTTP-Authentifizierung zeigt. Der JavaScript-Code zum Generieren des Tokens ist nicht geheim - nur das Benutzergeheimnis muss geheim sein.
Deamon
3
@Sri, obwohl ich damit einverstanden bin, dass eine Sitzung der beste Weg ist, dies aus Sicherheits- und Leistungsgründen zu handhaben. Dies ist nicht RESTful, da der Server den Status pro Benutzer verfolgen muss, was zu Problemen bei der Skalierbarkeit führen kann.
Turm
1
Ist es richtig zu sagen, dass die Übermittlung von doppelten Cookies nicht funktioniert, wenn die Seite für XSS-Angriffe anfällig ist? Denn dann könnten Sie ein Formular direkt aus der Domain heraus senden und der Wert würde sowohl per Cookie als auch per Formular gesendet.
Gabriele Cirulli
1
@ GabrieleCirulli Ja, es ist eine faire Aussage. XSS übertrifft die meisten CSRF-Schutzfunktionen. Captchas sind vielleicht die einzige Form von CSRF, die noch wirksam ist.
Sripathi Krishnan
Du meinst CSRF-Schutz? : P Aber ja, ich stimme zu.
Gabriele Cirulli
6

Die statische Formular-ID bietet überhaupt keinen Schutz. Ein Angreifer kann es selbst holen. Denken Sie daran, dass der Angreifer nicht auf die Verwendung von JavaScript auf dem Client beschränkt ist. Er kann die statische Formular-ID serverseitig abrufen.

Ich bin mir nicht sicher, ob ich die vorgeschlagene Verteidigung vollständig verstehe. Woher kommt das GET /usersecret/john_doe? Ist das Teil der Seite JavaScript? Ist das die wörtlich vorgeschlagene URL? Wenn ja, gehe ich davon aus, dass dies usernamekein Geheimnis ist, was bedeutet, dass evil.ru Benutzergeheimnisse wiederherstellen kann, wenn ein Browser- oder Plugin-Fehler domänenübergreifende GET-Anforderungen zulässt. Warum nicht das Benutzergeheimnis bei der Authentifizierung in einem Cookie speichern, anstatt es von jedem abrufen zu lassen, der domänenübergreifende GETs ausführen kann?

Ich habe "Robuste Abwehrmechanismen für standortübergreifende Fälschungen" sehr sorgfältig gelesen, bevor ich mein eigenes Authentifizierungssystem implementiert habe, das gegen CSRF resistent sein soll. Tatsächlich würde ich die Implementierung meines eigenen Authentifizierungssystems überhaupt überdenken.

Scott Wolchok
quelle
1
Die Formular-ID ist so etwas wie ein öffentlicher Schlüssel. Sie haben Recht, GET /usersecret/john_doeist Teil des JavaScript. Der Benutzername selbst ist nicht das Geheimnis, sondern die ID, die mit dieser Anforderung von einem authentifizierten (!) Benutzer abgerufen wird. Danke für den Link.
Deamon
3

Es gibt einige Methoden im CSRF-Präventions-Spickzettel , die von einem erholsamen Dienst verwendet werden können. Die RESTful zustandslose CSRF-Minderung besteht darin, den Origin- oder HTTP-Referer zu verwenden, um sicherzustellen, dass die Anforderungen von einer Domäne stammen, der Sie vertrauen.

Turm
quelle
2
Dies ist ein gefährlicher Ratschlag, obwohl es schwierig ist, den HTTP-Referer zu fälschen. Es ist nicht unmöglich, auch der Referer-Header kann nicht garantiert werden (und das Nicht-Senden eines Referer-Headers führt zum Bruch Ihrer App).
Junge Baukema
3
@RelaXNow Schauen Sie sich dieses CSRF-Exploit-Framework an, das ich geschrieben habe: github.com/TheRook/CSRF-Request-Builder . Hier können Sie beliebige http-Header sowie Body angeben. Der http-Verweis kann jedoch nicht geändert werden, da dies von Flash gesperrt wird. Der Spickzettel zur CSRF-Prävention ist sehr gut. Sie sollten den Link in meinem Beitrag lesen.
Turm
Fairerweise kann ein Angreifer im Kontext von CSRF (soweit ich weiß) den Referer-Header des Opfers nicht fälschen, der Header ist jedoch immer noch nicht garantiert und sollte für Ihre API nur erforderlich sein, wenn Sie dies können garantieren, dass es immer gesendet wird (wie bei einer internen Bewerbung für ein Unternehmen).
Junge Baukema
3
@RelaXNow Wenn die Anforderung von einer HTTPS-Seite stammt, wird der Referer von der Anforderung festgeschrieben. Dies sollte als Fehler angesehen werden (siehe Link oben). Die Leute arbeiten an diesem Problem. Mozilla hat den http-Header "Origin" eingeführt, der großartig und sehenswert ist. Er kann nicht nur zur Lösung dieses Problems des RESTful-CSRF-Schutzes verwendet werden, sondern auch für viele andere Missbräuche wie JSON-Inklusionsangriffe und Click-Jacking. Das Problem ist, dass nicht jeder Browser es unterstützt :(. Auch ich habe meinen Beitrag bearbeitet, nur für den Fall, dass Sie den -1.
Turm
1
s / comitted / weggelassen / :). Gute Punkte / Informationen, aber ich habe meine -1 vor langer Zeit zurückgezogen und Ihre Kommentare für hilfreiche Informationen positiv bewertet.
Junge Baukema
0

Stimmt etwas mit diesem Ansatz nicht, obwohl er ohne JavaScript nicht funktioniert?

Ihr Benutzergeheimnis ist kein Geheimnis, wenn Sie es an den Client senden. Normalerweise verwenden wir solche Geheimnisse, um Hashes zu generieren, sie mit dem Formular zu senden und sie zum Vergleich zurück zu warten.

Wenn Sie RESTful sein möchten, muss die Anforderung alle Informationen zur Verarbeitung enthalten. So können Sie das machen:

  • Fügen Sie Ihrem REST-Client ein CSRF-Token-Cookie hinzu und senden Sie dasselbe Token in versteckter Eingabe mit Ihren Formularen. Wenn sich der Dienst und der Client unter verschiedenen Domänen befinden, müssen Sie die Anmeldeinformationen freigeben. Auf dem Dienst müssen Sie die 2 Token vergleichen, und wenn sie gleich sind, ist die Anfrage gültig ...

  • Sie können das csrf-Token-Cookie mit Ihrem REST-Service hinzufügen und dasselbe Token mit den Darstellungen Ihrer Ressourcen (versteckte Eingaben usw.) senden. Alles andere ist das gleiche wie das Ende der vorherigen Lösung. Diese Lösung steht am Rande von RESTfulness. (Es ist in Ordnung, bis der Client den Dienst nicht aufruft, um das Cookie zu ändern. Wenn das Cookie nur http ist, sollte der Client nichts davon wissen. Wenn dies nicht der Fall ist, sollte der Client es festlegen.) Sie können weitere Schritte ausführen Komplexe Lösung, wenn Sie jedem Formular unterschiedliche Token hinzufügen und den Cookies eine Ablaufzeit hinzufügen. Sie können die Ablaufzeit auch mit den Formularen zurücksenden, damit Sie den Grund kennen, warum eine Token-Validierung fehlschlägt.

  • Sie können ein Benutzergeheimnis (von Benutzer zu Benutzer unterschiedlich) im Ressourcenstatus Ihres Dienstes festlegen. Durch Erstellen von Darstellungen können Sie für jedes Formular ein Token (und eine Ablaufzeit) generieren. Sie können einen Hash aus dem tatsächlichen Token (und der Ablaufzeit, der Methode, der URL usw.) und dem Benutzergeheimnis generieren und diesen Hash auch mit dem Formular senden. Sie halten den "Benutzer geheim" natürlich geheim, also senden Sie das nie mit dem Formular. Wenn Ihr Dienst danach eine Anfrage erhält, können Sie den Hash aus den Anforderungsparametern und dem Benutzergeheimnis erneut generieren und vergleichen. Wenn die nicht übereinstimmen, ist die Anfrage ungültig ...

Keiner von ihnen schützt Sie, wenn Ihr REST-Client Javascript injizierbar ist. Sie müssen daher den gesamten Benutzerinhalt mit HTML-Entitäten vergleichen und alle entfernen oder TextNodes immer anstelle von innerHTML verwenden. Sie müssen sich auch vor SQL-Injection und HTTP-Header-Injection schützen. Verwenden Sie niemals einfaches FTP, um Ihre Site zu aktualisieren. Und so weiter ... Es gibt viele Möglichkeiten, bösen Code in Ihre Site einzufügen ...

Fast hätte ich vergessen zu erwähnen, dass GET-Anfragen immer vom Dienst und auch vom Kunden gelesen werden können. Für den Dienst ist dies offensichtlich. Wenn der Client eine URL im Browser festlegt, die eine Darstellung einer Ressource oder mehrerer Ressourcen ergeben muss, sollte er niemals eine POST / PUT / DELETE-Methode für eine Ressource aufrufen. Zum Beispiel GET http://my.client.com/resource/delete -> DELETE http://my.api.com/resourceist eine sehr, sehr schlechte Lösung. Dies ist jedoch eine sehr grundlegende Fähigkeit, wenn Sie CSRF behindern möchten.

inf3rno
quelle