Rails 5 API protect_from_forgery

匿名 (未验证) 提交于 2019-12-03 02:57:02

问题:

I have a Rails 5 API app (ApplicationController < ActionController::API). The need came up to add a simple GUI form for one endpoint of this API.

Initially I was getting ActionView::Template::Error undefined method protect_against_forgery? when I tried to render the form. I added include ActionController::RequestForgeryProtection and protect_from_forgery with:exception to that endpoint. Which solved that issue as expected.

However, when I try to submit this form I get: 422 Unprocessable Entity ActionController::InvalidAuthenticityToken. I've added <%= csrf_meta_tags %> and verified that meta: csrf-param and meta: csrf-token are present in my headers, and that authenticity_token is present in my form. (The tokens themselves are different from each other.)

I've tried, protect_from_forgery prepend: true, with:exception, no effect. I can "fix" this issue by commenting out: protect_from_forgery with:exception. But my understanding is that that is turning off CSRF protection on my form. (I want CSRF protection.)

What am I missing?

UPDATE:

To try to make this clear, 99% of this app is a pure JSON RESTful API. The need came up to add one HTML view and form to this app. So for one Controller I want to enable full CSRF protection. The rest of the app doesn't need CSRF and can remain unchanged.

UPDATE 2:

I just compared the page source of this app's HTML form and Header with another conventional Rails 5 app I wrote. The authenticity_token in the Header and the authenticity_token in the form are the same. In the API app I'm having the problem with, they're different. Maybe that's something?

UPDATE 3:

Ok, I don't the the mismatch is the issue. However, in further comparisons between the working and non-working apps I noticed that there's nothing in Network > Cookies. I see a bunch of things like _my_app-session in the cookies of the working app.

回答1:

Here's what the issue was: Rails 5, when in API mode, logically doesn't include the Cookie middleware. Without it, there's no Session key stored in a Cookie to be used when validating the token I passed with my form.

Somewhat confusingly, changing things in config/initializers/session_store.rb had no effect.

I eventually found the answer to that problem here: Adding cookie session store back to Rails API app, which led me here: https://github.com/rails/rails/pull/28009/files which mentioned exactly the lines I needed to add to application.rb to get working Cookies back:

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 

Those three lines coupled with:

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

And of course a form generated through the proper helpers:

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

Got me a CSRF protected form in the middle of my Rails API app.



回答2:

If you're using Rails 5 API mode, you do not use protect_from_forgery or include <%= csrf_meta_tags %> in any view since your API is 'stateless'. If you were going to use full Rails (not API mode) while ALSO using it as a REST API for other apps/clients, then you could do something like this:

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

So that protect_from_forgery would be called when appropriate. But I see ActionController::API in your code so it appears you're using API mode in which case you'd remove the method from your application controller altogether



回答3:

No need of protect_from_forgery for AJAX calls and apis.

If you want to disable it for some action then

protect_from_forgery except: ['action_name'] 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!