web-dev-qa-db-ger.com

Rails 5 API protect_from_forgery

Ich habe eine Rails 5-API-App (ApplicationController < ActionController::API). Es bestand die Notwendigkeit, ein einfaches GUI-Formular für einen Endpunkt dieser API hinzuzufügen. 

Anfangs bekam ich ActionView::Template::Error undefined method protect_against_forgery?, als ich versuchte, das Formular zu rendern. Ich habe diesem Endpunkt include ActionController::RequestForgeryProtection und protect_from_forgery with:exception hinzugefügt. Welches Problem wie erwartet gelöst. 

Wenn ich jedoch versuche, dieses Formular zu übermitteln, erhalte ich Folgendes: 422Unprocessable EntityActionController::InvalidAuthenticityToken. Ich habe <%= csrf_meta_tags %> hinzugefügt und festgestellt, dass meta: csrf-param und meta: csrf-token in meinen Kopfzeilen vorhanden sind und dass authenticity_token in meinem Formular vorhanden ist. (Die Token selbst unterscheiden sich voneinander.)

Ich habe versucht, protect_from_forgery prepend: true, with:exception, keine Wirkung. Ich kann dieses Problem "beheben", indem ich Folgendes auskommentiere: protect_from_forgery with:exception. Ich verstehe jedoch, dass dies den CSRF-Schutz in meinem Formular deaktiviert. (Ich möchte CSRF-Schutz.)

Was vermisse ich? 

UPDATE:

Um dies deutlich zu machen, handelt es sich bei 99% dieser App um eine reine JSON-RESTful-API. Es bestand die Notwendigkeit, dieser App eine HTML-Ansicht und ein Formular hinzuzufügen. Also für einen Controller möchte ich vollen CSRF-Schutz aktivieren. Der Rest der App benötigt keine CSRF und kann unverändert bleiben.

UPDATE 2:

Ich habe die Seitenquelle des HTML-Formulars und der Kopfzeile dieser App mit einer anderen herkömmlichen Rails 5-App verglichen, die ich geschrieben habe. Der authenticity_token in der Kopfzeile und der authenticity_token im Formular sind gleich. In der API-App habe ich das Problem, sie sind different. Vielleicht ist das etwas?

UPDATE 3:

Ok, das ist nicht das Problem. Bei weiteren Vergleichen zwischen den funktionierenden und den nicht funktionierenden Apps ist mir jedoch aufgefallen, dass unter Netzwerk> Cookies nichts enthalten ist. Ich sehe eine Menge Dinge wie _my_app-session in den Cookies der funktionierenden App.

14
lostphilosopher

Hier ist das Problem: Rails 5 enthält im API-Modus die Cookie-Middleware logischerweise nicht. Ohne diese Option wird in einem Cookie keine Session key gespeichert, die bei der Überprüfung des Tokens verwendet werden soll, das ich mit meinem Formular übergeben habe. 

Etwas verwirrend war, dass Änderungen in config/initializers/session_store.rb keine Auswirkung hatten. 

Ich fand schließlich die Antwort auf dieses Problem hier: Hinzufügen von Cookiesitzungsspeicher zurück zur Rails API-App , was mich hierher führte: https://github.com/Rails/rails/pull/28009/files die genau die Zeilen erwähnte, die ich zu application.rb hinzufügen musste, um wieder funktionierende Cookies zu erhalten:

config.session_store :cookie_store, key: "_YOUR_APP_session_#{Rails.env}"
config.middleware.use ActionDispatch::Cookies # Required for all session management
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options

Diese drei Linien gekoppelt mit:

class FooController < ApplicationController
  include ActionController::RequestForgeryProtection
  protect_from_forgery with: :exception, unless: -> { request.format.json? }
  ...

Und natürlich ein Formular, das von den richtigen Helfern erstellt wurde:

form_tag(FOO_CREATE_path, method: :post)
  ...

Ich habe mir ein CSRF-geschütztes Formular mitten in meiner Rails-API-App gesichert.

15
lostphilosopher

Wenn Sie den API-Modus von Rails 5 verwenden, verwenden Sie in keiner Ansicht protect_from_forgery oder schließen <%= csrf_meta_tags %> ein, da Ihre API 'stateless' ist. Wenn Sie volle Rails (nicht den API-Modus) verwenden würden, während Sie ALSO als REST - API für andere Apps/Clients verwenden, können Sie Folgendes tun:

protect_from_forgery unless: -> { request.format.json? }

Damit würde protect_from_forgery gegebenenfalls aufgerufen werden. Ich sehe jedoch ActionController::API in Ihrem Code, so dass Sie den API-Modus verwenden. In diesem Fall entfernen Sie die Methode vollständig von Ihrem Anwendungscontroller 

8
Ricky Brown

Keine Notwendigkeit von protect_from_forgery für AJAX Aufrufe und apis.

Wenn Sie es für einige Aktionen deaktivieren möchten, dann

protect_from_forgery except: ['action_name']
3
puneet18
class Api::ApiController < ApplicationController
  skip_before_action :verify_authenticity_token
end

Verwendung wie oben bei Rails 5

0
Muktesh Kumar