Подтвердить что ты не робот

Rails 5 API protect_from_forgery

У меня есть приложение Rails 5 API (ApplicationController < ActionController::API). Пришла необходимость добавить простую форму GUI для одной конечной точки этого API.

Первоначально я получал 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-защиту.)

Что мне не хватает?

UPDATE:

Чтобы сделать это ясно, 99% этого приложения является чистым JSON RESTful API. Пришла необходимость добавить один HTML-вид и форму в это приложение. Поэтому для одного контроллера я хочу включить полную защиту CSRF. Остальное приложение не нуждается в CSRF и может оставаться неизменным.

ОБНОВЛЕНИЕ 2:

Я просто сравнил источник страницы этой формы HTML приложения и заголовка с другим обычным приложением Rails 5, которое я написал. authenticity_token в заголовке и authenticity_token в форме одинаковы. В приложении API у меня проблема, они разные. Может быть, что-то?

ОБНОВЛЕНИЕ 3:

Хорошо, это не проблема. Однако при дальнейшем сравнении между работающими и нерабочими приложениями я заметил, что в Network > Cookies нет ничего. Я вижу кучу таких вещей, как _my_app-session в файлах cookie рабочего приложения.

4b9b3361

Ответ 1

Вот в чем проблема: Rails 5, когда в режиме API логически не включает промежуточное ПО Cookie. Без него нет сеанса key, хранящегося в Cookie, который будет использоваться при проверке маркера, который я передал с моей формой.

Несколько смехотворно, изменение вещей в config/initializers/session_store.rb не имело никакого эффекта.

В итоге я нашел ответ на эту проблему: Добавление хранилища сеансов cookie обратно в приложение Rails API, которое привело меня сюда: https://github.com/rails/rails/pull/28009/files, в котором были указаны именно те строки, которые мне нужно было добавить в application.rb, чтобы получить рабочие Cookies назад:

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

Эти три строки в сочетании с:

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

И, конечно, форма, созданная через правильные помощники:

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

Получил защищенную CSRF форму в середине моего приложения API Rails.

Ответ 2

Если вы используете режим Rails 5 API, вы не используете protect_from_forgery или include <%= csrf_meta_tags %> в любом представлении, так как ваш API является "без гражданства". Если вы собираетесь использовать полный Rails (не API-режим), в то время как ALSO использует его как REST API для других приложений/клиентов, вы можете сделать что-то вроде этого:

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

Чтобы protect_from_forgery был вызван, когда это необходимо. Но я вижу ActionController::API в вашем коде, поэтому, похоже, вы используете режим API, и в этом случае вы полностью удаляете этот метод со своего контроллера приложения.

Ответ 3

Нет необходимости защищать_from_forgery для вызовов AJAX и apis.

Если вы хотите отключить его для некоторых действий, то

protect_from_forgery except: ['action_name']

Ответ 4

class Api::ApiController < ApplicationController
  skip_before_action :verify_authenticity_token
end

Используйте как выше с рельсами 5