Rails 5 APIアプリ(ApplicationController < ActionController::API
)があります。このAPIの1つのエンドポイントにシンプルなGUIフォームを追加する必要が生じました。
最初は、フォームをレンダリングしようとしたときにActionView::Template::Error undefined method protect_against_forgery?
を取得していました。 include ActionController::RequestForgeryProtection
とprotect_from_forgery with:exception
をそのエンドポイントに追加しました。期待どおりにその問題を解決しました。
ただし、このフォームを送信しようとすると、422
Unprocessable Entity
ActionController::InvalidAuthenticityToken
が返されます。 <%= csrf_meta_tags %>
を追加し、meta: csrf-param
とmeta: csrf-token
がヘッダーに存在し、authenticity_token
がフォームに存在することを確認しました。 (トークン自体は互いに異なります。)
試してみましたが、protect_from_forgery prepend: true, with:exception
、効果はありません。この問題をコメントアウトすることで「修正」できます:protect_from_forgery with:exception
。しかし、私の理解では、それは私のフォームでCSRF保護をオフにしているということです。 (CSRF保護が必要です。)
私は何が欠けていますか?
更新:
これを明確にするために、このアプリの99%は純粋なJSON RESTful APIです。このアプリに1つのHTMLビューとフォームを追加する必要が生じました。したがって、1つのコントローラーに対して完全なCSRF保護を有効にしたいと思います。アプリの残りの部分はCSRFを必要とせず、変更せずに残すことができます。
更新2:
このアプリのHTMLフォームとヘッダーのページソースを、私が書いた別の従来のRails 5アプリと比較しました。ヘッダーのauthenticity_token
とフォームのauthenticity_token
aresame。私が問題を抱えているAPIアプリでは、それらはdifferent。たぶんそれは何か?
更新3:
わかりました、私は不一致が問題ではありません。ただし、動作中のアプリと動作していないアプリをさらに比較すると、[ネットワーク]> [Cookies]に何もないことがわかりました。動作中のアプリのCookieに_my_app-session
のようなものがたくさんあります。
問題は次のとおりです。Rails 5、APIモードの場合、論理的にはCookieミドルウェアは含まれません。これがない場合、Cookieに保存されるSession key
フォームで渡したトークンを検証するときに使用されます。
やや紛らわしいことに、config/initializers/session_store.rb
は効果がなかった。
最終的にここでその問題の答えを見つけました: CookieセッションストアをRails API app に追加し直しました。ここ: https://github.com/Rails/rails/pull/28009/files application.rbに追加する必要がある行を正確に言及しました動作しているCookieを取得するには:
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
これらの3行は次と結合されます。
class FooController < ApplicationController
include ActionController::RequestForgeryProtection
protect_from_forgery with: :exception, unless: -> { request.format.json? }
...
そしてもちろん、適切なヘルパーによって生成されたフォーム:
form_tag(FOO_CREATE_path, method: :post)
...
Rails APIアプリの途中でCSRF保護フォームを取得しました。
Rails 5 APIモードを使用している場合、protect_from_forgery
または含める<%= csrf_meta_tags %>
は、APIが「ステートレス」であるため、どのビューでも表示されます。完全なRails(APIモードではない)を使用する一方で、それをREST他のアプリ/クライアントのAPIとして使用する場合)このような:
protect_from_forgery unless: -> { request.format.json? }
そのため protect_from_forgery
は適切なときに呼び出されます。しかし、ActionController::API
コード内で、APIモードを使用しているように見えます。この場合、アプリケーションコントローラーからメソッドを完全に削除します。
AJAX呼び出しとAPIの場合、protect_from_forgeryは不要です。
何らかのアクションのためにそれを無効にしたい場合は、
protect_from_forgery except: ['action_name']
class Api::ApiController < ApplicationController
skip_before_action :verify_authenticity_token
end
上記と同じようにRails 5