devise icon indicating copy to clipboard operation
devise copied to clipboard

Can't verify CSRF token in production

Open valentin2105 opened this issue 4 years ago • 11 comments

Hello,

Environment

  • Ruby 2.7.1p83
  • Rails 6.0.3.3
  • Devise 4.7.3

Current behavior

When I setup my app in production mode (in local or in Docker with Postgres), I cannot create a new user and/or login. here is the logs :

(Everything work fine in Dev environment. )

I, [2020-10-08T15:14:27.343554 #64223]  INFO -- : [fade9dc3-0be8-4a70-a2b8-78a8bd2420ac] Started POST "/users" for 127.0.0.1 at 2020-10-08 15:14:27 +1100
I, [2020-10-08T15:14:27.344406 #64223]  INFO -- : [fade9dc3-0be8-4a70-a2b8-78a8bd2420ac] Processing by Devise::RegistrationsController#create as HTML
I, [2020-10-08T15:14:27.344478 #64223]  INFO -- : [fade9dc3-0be8-4a70-a2b8-78a8bd2420ac]   Parameters: {"authenticity_token"=>"filtered", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Confirmer"}
W, [2020-10-08T15:14:27.345492 #64223]  WARN -- : [fade9dc3-0be8-4a70-a2b8-78a8bd2420ac] Can't verify CSRF token authenticity.
I, [2020-10-08T15:14:27.345835 #64223]  INFO -- : [fade9dc3-0be8-4a70-a2b8-78a8bd2420ac] Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms | Allocations: 952)
F, [2020-10-08T15:14:27.346608 #64223] FATAL -- : [fade9dc3-0be8-4a70-a2b8-78a8bd2420ac]

Expected behavior

Be able to create an account in production environment.

Files :

config/application.rb

require_relative 'boot'
require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Sauron
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0
    config.application = config_for(:application)
    config.time_zone = 'Pacific/Noumea'

    #config.middleware.insert_after ActionDispatch::Static, Rack::Deflater
    config.i18n.fallbacks = [I18n.default_locale]

    config.hosts << Rails.configuration.application['HOST_DOMAIN']

  end
end

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
    before_action :authenticate_user!
    protect_from_forgery with: :exception
    before_action :configure_permitted_parameters, if: :devise_controller?

    def get_vm(vmid)
   ...

vars :

APP_SESSION_KEY=""
SECRET_KEY_BASE="03b41cea8586bb5280615a36bf71a5276ac7e05d13200ba0e54d846766f9b14b74419664528180d2d8795c52cf6f6bce40ba86c5c83ae1d11a7cdf3083FILTERED"
DB_PASSWORD=filtered
DB_USER=filtered
DB_HOST=localhost
RAILS_ENV=production
DB_NAME=filtered

Thank you !

valentin2105 avatar Oct 08 '20 04:10 valentin2105

Hi @valentin2105, thanks for your submission. Are you able to submit other forms? If so, can you provide a sample app that shows the issue with just Rails & Devise?

We've had similar reports of CSRF / AuthenticityToken errors in the past (feel free to search around the issues tracker, it might help) and it always ends up being something else with the environment, another gem interfering, or Rails itself, but never Devise, as it does nothing "special" regarding CSRF that'd cause invalidation/errors.

carlosantoniodasilva avatar Oct 08 '20 11:10 carlosantoniodasilva

Hi @carlosantoniodasilva , I can't try other form because of they are all behind the login form. Thanks for taking time to take a look,

Here his the mostly empty project, https://github.com/valentin2105/rails-prod-bug

you can clone it and then,

mv config/application.yml.example config/application.yml
bundle
yarn install --check-files
bundle exec rails db:migrate
bundle exec rails s -p 3000 

Then you can test login in development, that should work with sqlite.

To try in production (you need a PG DB) :

export DB_PASSWORD=PassW0rd
export DB_USER=app
export DB_HOST=localhost
export DB_NAME=apptest 
export SECRET_KEY_BASE=03b41cea8586bb5280615a36bf71a5276ac7e05d13200ba0e54d846766f9b14b74419664528180d2d8795c52cf6f6bce40ba86c5c83ae1d11a7cdf3083a10799
export RAILS_ENV=production
bundle exec rails db:migrate
bundle exec rails s -p 3000 

In production you shouldn't be able to create an account, logs are available at logs/production.log

valentin2105 avatar Oct 08 '20 21:10 valentin2105

Hi, @valentin2105 did you manage to overcome your problem? I have a similar problem which happened during upgrade rails from 6.0.1 to 6.0.3.4. InvalidAuthenticityToken error is happening also on development environment while I use ngrok to simulate real calls.

mRudzki avatar Nov 02 '20 09:11 mRudzki

Hi @mRudzki ,

I didn't find any solution on this. @carlosantoniodasilva can you take a quick look in this ?

Thanks a lot !

valentin2105 avatar Nov 02 '20 22:11 valentin2105

I'm having a similar issue with this, and it seems related to the per_form_csrf_tokens option added as default on rails 5. When this option is enabled, the authenticity_token embedded on the form differs to the one added on the layout by default, as the last one is globally valid, while the per-form is only valid there.

It seems that it is quite common to skip the line <%= csrf_meta_tags %> so many applications fallback to the form hidden input.

But it seems that this per-form-token is more fragile and it fails when signing_in on some escenarios (it happens to some users, while most users log-in without any issues). I couldn't reproduce the bug myself.

eloyesp avatar Jan 28 '21 15:01 eloyesp

Is this something that only happens with Devise? I am having a hard time thinking how it could be Devise specific to be honest, but I haven't been able to circle back on this and try to investigate more thoroughly. (and if I'm being honest, I have a list of Devise issues to work through before I can get back to this one, but I wanted to leave a note here with that thought.)

carlosantoniodasilva avatar Jan 28 '21 18:01 carlosantoniodasilva

Yes, it happens only with devise endpoints... I think there are two possible reasons, there is a hook that invalidates the token, so there might be a timing issue and devise does use a different layout, so the head token might not be used.

Also, the action and controller naming on devise is a bit special, and the form token might not work on this case.

El 28 de enero de 2021 15:09:49 ART, Carlos Antonio da Silva [email protected] escribió:

Is this something that only happens with Devise? I am having a hard time thinking how it could be Devise specific to be honest, but I haven't been able to circle back on this and try to investigate more thoroughly. (and if I'm being honest, I have a list of Devise issues to work through before I can get back to this one, but I wanted to leave a note here with that thought.)

-- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/heartcombo/devise/issues/5298#issuecomment-769274315 -- Enviado desde mi dispositivo Android con K-9 Mail. Por favor, disculpa mi brevedad.

eloyesp avatar Jan 28 '21 19:01 eloyesp

@valentin2105 did you ever resolve your csrf issue?

Not sure if this helps, but the devise readme docs state:

For Rails 5, note that protect_from_forgery is no longer prepended to the before_action chain, so if you have set authenticate_user before protect_from_forgery, your request will result in "Can't verify CSRF token authenticity." To resolve this, either change the order in which you call them, or use protect_from_forgery prepend: true.

From your example code, it looks like you may need to change the order of authenticate_user and protect_from_forgery.

nCubed avatar Oct 08 '21 21:10 nCubed

What solved it for me was to add the following to settings.py, replacing "<my_domain>" part of course.

CSRF_TRUSTED_ORIGINS = ['https://<my_domain>.com']

MARKOTHEDEV avatar Jun 10 '22 23:06 MARKOTHEDEV

I had a similar issue. It was connected to malformed cookies for rack 3 type applications. I had to upgrade puma to the latest version.

chbach avatar Oct 23 '23 19:10 chbach

Check a frequent misstep involves misconfiguring CSRF protection when integrating Devise. Previously, protect_from_forgery was automatically prepended to the before_action chain, ensuring CSRF tokens were verified before any controller action. However, this is no longer the default behavior, see https://github.com/heartcombo/devise?tab=readme-ov-file#controller-filters-and-helpers.

Ensure protect_from_forgery is correctly configured to precede any authentication filters, specifically authenticate_user!. By using the prepend: true option, you can ensure that every request is authenticated and verified for CSRF tokens in the correct sequence, maintaining the security integrity of your application.

So, change

protect_from_forgery with: :exception

to

protect_from_forgery with: :exception, prepend: true

Possible duplicates: https://github.com/heartcombo/devise/issues/2734 https://github.com/heartcombo/devise/issues/5652

viktorianer avatar Feb 22 '24 15:02 viktorianer