Приложение 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
.