Schienen: Die CSRF-Token-Authentizität kann bei einer POST-Anforderung nicht überprüft werden

88

Ich möchte POST requestmeinem lokalen Entwickler Folgendes mitteilen:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

Über die Serverkonsole wird jedoch berichtet

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

Hier ist mein Controller und Routen-Setup, es ist ganz einfach.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

Ich bin mir nicht sicher, was ich tun muss? Das Ausschalten der CSRF würde sicherlich funktionieren, aber ich denke, es sollte mein Fehler sein, wenn ich eine solche API erstelle.

Muss ich noch etwas einrichten?

cqcn1991
quelle
5
Für APIs wird allgemein akzeptiert, dass die CSRF- Token-Validierung deaktiviert wird . Ich benutze protect_from_forgery with: :null_session.
Dcestari

Antworten:

109

Cross Site Request Forgery (CSRF / XSRF) ist, wenn eine böswillige Webseite Benutzer dazu verleitet, eine Anforderung auszuführen, die nicht beabsichtigt ist, z. B. mithilfe von Lesezeichen, Iframes oder einfach durch Erstellen einer Seite, die visuell ähnlich genug ist, um Benutzer zu täuschen.

Der Rails CSRF-Schutz wurde für "klassische" Web-Apps entwickelt. Er bietet lediglich ein gewisses Maß an Sicherheit, dass die Anforderung von Ihrer eigenen Web-App stammt. Ein CSRF-Token funktioniert wie ein Geheimnis, das nur Ihr Server kennt. Rails generiert ein zufälliges Token und speichert es in der Sitzung. Ihre Formulare senden das Token über eine versteckte Eingabe, und Rails überprüft, ob jede Nicht-GET-Anforderung ein Token enthält, das mit dem übereinstimmt, was in der Sitzung gespeichert ist.

Eine API ist jedoch in der Regel per Definition standortübergreifend und soll in mehr als Ihrer Web-App verwendet werden. Dies bedeutet, dass das gesamte Konzept von CSRF nicht ganz anwendbar ist.

Stattdessen sollten Sie eine tokenbasierte Strategie zur Authentifizierung von API-Anforderungen mit einem API-Schlüssel und einem Geheimnis verwenden, da Sie überprüfen, ob die Anforderung von einem genehmigten API-Client stammt - nicht von Ihrer eigenen App.

Sie können CSRF deaktivieren, wie von @dcestari angegeben:

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

Aktualisiert. In Rails 5 können Sie mithilfe der folgenden --apiOption nur API-Anwendungen generieren :

rails new appname --api

Sie enthalten nicht die CSRF-Middleware und viele andere Komponenten, die überflüssig sind.

max
quelle
Vielen Dank, ich entscheide mich, einen Teil der CSRF auszuschalten: stackoverflow.com/questions/5669322/…
cqcn1991
4
Dies deutet darauf hin, dass alle APIs den Datenverkehr von Anwendung zu Anwendung bedienen (bei dem einem einzelnen Partner ein eindeutiger Schlüssel und ein Geheimnis zugewiesen werden). In diesem Szenario ist diese Antwort wie bei einer Server-zu-Server-Kommunikation angemessen. Für die meisten Web-App-Entwickler ist es jedoch verwirrend, dass Sie für einen von Ihnen kontrollierten und geschriebenen Javascript-Client KEIN einziges Schlüsselgeheimnis verwenden möchten (das allen Clients ein einziges Schlüsselgeheimnis zugänglich machen würde). Stattdessen funktionieren der CSRF- und Cookie-Sitzungsmechanismus von Rail hervorragend - auch für Javascript-Apps, die Ihre API verwenden -, wenn Sie das CSRF-Token bei jeder Anforderung an Rails zurückgeben.
Jason FB
Die Art und Weise, wie Sie dies in AJAX tun, wird in diesem SO-Beitrag beschrieben. stackoverflow.com/questions/7203304/…
Jason FB
@JasonFB Sie haben Recht, dass ein einzelnes Geheimnis mit Javascript-Clients nicht funktioniert. Die Verwendung von Sitzungen und des Rails-CSRF-Schutzes ist jedoch weiterhin problematisch, wenn Sie eine API erstellen möchten, die auch von anderen Arten von Nicht-Browser-Clients verwendet werden kann.
Max
Wenn Sie eine Alternative zu CSRF anbieten oder bauen, seien Sie mein Gast. Kennen Sie einfach den Grund für das, was Sie ersetzen, damit Sie wissen, durch was es ersetzt werden kann. Offensichtlich wird unser Streit jetzt kontextuell.
Jason FB
76

Eine andere Möglichkeit, CSRF zu deaktivieren, die keine Nullsitzung rendert, besteht darin, Folgendes hinzuzufügen:

skip_before_action :verify_authenticity_token

in Ihrem Rails Controller. Dadurch wird sichergestellt, dass Sie weiterhin Zugriff auf Sitzungsinformationen haben.

Stellen Sie erneut sicher, dass Sie dies nur in API-Controllern oder an anderen Orten tun, an denen der CSRF-Schutz nicht vollständig angewendet wird.

Matt Waldron
quelle
1
Das ist das gleiche als protect_from_forgery except: [:my_method_name]?
Arnold Roa
19

Auf api.rubyonrails.org finden Sie relevante Informationen zu einer Konfiguration von CSRF in Bezug auf API-Controller :

Beachten Sie, dass auch XML- oder JSON-Anforderungen betroffen sind. Wenn Sie eine API erstellen, sollten Sie die Fälschungsschutzmethode in ApplicationController(standardmäßig :) ändern :exception:

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

Möglicherweise möchten wir den CSRF-Schutz für APIs deaktivieren, da diese normalerweise so konzipiert sind, dass sie keinen Status haben. Das heißt, der Anforderungs- API- Client übernimmt die Sitzung für Sie anstelle von Rails.

Herr Tao
quelle
4
Das ist so verwirrend. Ich schwanke zwischen Quellen, die besagen, dass protect_from_forgerydies für APIs noch erforderlich ist, und Quellen, die besagen, dass dies nicht der Fall ist. Was ist, wenn die API für eine App mit nur einer Seite vorgesehen ist, die das Sitzungscookie für die Benutzerauthentifizierung verwendet?
Wylliam Judd
Der CSRF-Mechanismus ist die integrierte Methode von Rails, um mit dem Angriffsvektor mithilfe von Sitzungscookies umzugehen. Der Mechanismus schützt Ihre Controller, während Sie neben (tatsächlich innerhalb) des Rails-Sitzungscookies arbeiten. Ohne protected_from_forgery, ohne es zu deaktivieren oder eine Ausnahme zu setzen, weisen Sie Rails an, diese Aktion NICHT mit den Informationen aus dem CSRF-Token (das aus dem Sitzungscookie stammt) zu schützen.
Jason FB
5
Ich denke, was verwirrend ist, ist, dass für die Rails-Dokumentation "API" eine Server-zu-Server-Anwendung bedeutet, die API-Anforderungen von vertrauenswürdigen Remote-Partnern (nicht allen Webbenutzern, die Ihre Site besuchen) empfängt. Geben Sie für diese Personen eindeutige Schlüssel-Geheimnis-Paare über einen sicheren, nicht hackbaren Mechanismus an. Bei modernen Webanwendungen, bei denen viele Benutzer Ihre Webanwendung über einen Webclient laden, verwenden Sie weiterhin eine Sitzung oder einen tokenbasierten Mechanismus, um jede Person, die Ihre Website besucht, eindeutig zu identifizieren. Wenn Sie also nicht EINEN ANDEREN Mechanismus verwenden (Json-Web-Token usw.), bleiben Sie bei den integrierten Funktionen von Rails.
Jason FB
1
Die Art und Weise, wie Sie dies in AJAX tun, wird in diesem SO-Beitrag beschrieben. stackoverflow.com/questions/7203304/…
Jason FB
11

Seit Rails 5 können Sie auch eine neue Klasse mit :: API anstelle von :: Base erstellen :

class ApiController < ActionController::API
end
Webaholik
quelle
3

Wenn Sie die Beispielaktion des Sample-Controllers ausschließen möchten

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

Sie können Anfragen von außen problemlos bearbeiten.

Ryosuke Hujisawa
quelle
0

Die einfachste Lösung für das Problem besteht darin, Standardaufgaben in Ihrem Controller zu erledigen, oder Sie können sie direkt in ApplicationController einfügen

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

Arish Khan
quelle