Приложение Rails не может проверять токен CSRF только на хроме

У меня есть приложение Rails, работающее в контейнере Docker. Я использую Devise для аутентификации и Rack::Cors для CORS.

На моей машине все в порядке. После развертывания, я могу GET страницу входа правильно, но когда я заполнить регистрационную форму и отправить ее, Chrome отвечает с пустой страницей и 422 (Unprocessable Entity) кодом статуса. Журналы Rails читают:

Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

Интересно, что в Firefox все работает гладко.

Я пробовал все, что мог найти о Rails, CORS, CSRF, но я не смог найти решение.

Я действительно не знаю, какая информация может быть уместной здесь, поэтому не стесняйтесь спрашивать подробности в комментариях, я отредактирую вопрос.

Ответ 1

Для чего это стоит, я оглянулся на свой код, чтобы узнать, как я решил проблему.

Я не смог найти чистое решение, поэтому я работал вокруг него, отключив проверку происхождения:

# config/initializers/csrf_workaround.rb

Rails.application.config.action_controller.forgery_protection_origin_check = false

Конечно, это вводит уязвимости безопасности, поэтому обязательно отправляйте свой собственный ответ, если у вас есть более чистый способ заставить это работать и/или иметь реальное объяснение вопроса выше.

Ответ 2

Итак, у меня была похожая проблема; только у меня не было Devise или Docker. Это была простая форма. В вашем вопросе отсутствует много контекстной информации, такой как журналы, поэтому я не могу сказать, есть ли у вас такая же проблема, но вот как я ее исправил.

Я получал ошибки InvalidAuthenticityToken за простую отправку формы. Это озадачивает, так как он отлично работал в Firefox, но иногда приводил к случайным сбоям в Chrome, а в Chrome на Android он всегда приводил к ошибкам.

Диагностика

Я взглянул на журнал и обнаружил следующее:

Started POST "/invitations" for 172.69.39.15 at 2019-09-26 22:34:26 +0000
Processing by InvitationsController#create as HTML
  Parameters: {"authenticity_token"=>"F4ToAfkdPSnJsYewqvxXpsze3XitKHbiGnuEOR+628SdAY5jGRiG15GEuCSSoaVeVdO7eugAnsjKwmZPUpIepg==", "invitation"=>{"name"=>"[FILTERED]", "business"=>"[FILTERED]", "email"=>"[FILTERED]"}, "commit"=>"Apply for invite"}
HTTP Origin header (https://www.example.com) didn't match request.base_url (http://www.example.com)
Completed 422 Unprocessable Entity in 4ms (ActiveRecord: 0.0ms | Allocations: 226)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

Выделенная строка:

HTTP Origin header (https://www.example.com) didn't match request.base_url (http://www.example.com)

https://www.example.com действительно не соответствует http://www.example.com, у первого есть SSL. Я направлял свое приложение через Cloudflare, поэтому у меня был SSL, но мое приложение ожидало request.base_url без SSL.

Решение

Вам нужно заставить ваше приложение использовать SSL. Это то, что я сделал, чтобы исправить это; Ваши точные шаги могут зависеть от вашей архитектуры. Поскольку я использовал Cloudflare, мне пришлось выполнить эти шаги в точном порядке, иначе мое приложение могло быть отключено в автономном режиме:

Во-первых: Я настроил SSL на своем сервере. В этом случае я использовал Heroku, который может использовать Let Encrypt для автоматической подготовки SSL.

Второе: я настроил мое приложение для принудительной SSL, добавив следующее в production.rb

config.force_ssl = true

В-третьих: Поскольку мне больше не нужно HTTP-соединение между моим сервером и Cloudflare, я переключил его с Flexible на Full.