Richard-Degenne
Richard-Degenne

Reputation: 2959

Rails app can't verify CSRF token on chrome only

I have a Rails app running in a Docker container. I use Devise for authenticating and Rack::Cors for CORS.

On my machine, everything is okay. Once deployed, I can GET the login page correctly, but when I fill in the login form and submit it, Chrome replies with a blank page and a 422 (Unprocessable Entity) status code. The Rails logs reads:

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

Interestingly enough, on Firefox, everything runs smoothly.

I've tried everything I could find about Rails, CORS, CSRF, but I wasn't able to find a solution.

I don't really know what kind of information can be relevant here, so feel free to ask for details in the comments, I'll edit the question.

Upvotes: 13

Views: 2648

Answers (3)

HenryDo
HenryDo

Reputation: 21

I have same problem when I change from http to https on my gitlab config. As you can see the log below.

Processing by Ldap::OmniauthCallbacksController#ldapmain as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "username"=>"user1", "password"=>"[FILTERED]"}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms | Elasticsearch: 0.0ms | Allocations: 308)
Processing by OmniauthCallbacksController#failure as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "username"=>"user1", "password"=>"[FILTERED]"}
Redirected to http://172.20.1.12/users/sign_in

User cannot login with LDAPs account. I found that this problem only in chrome (I try to use firefox and firefox work like charm). After update chrome to new version this problem was gone. So may be the solution is update chrome to the lastest version. I also try lots of solutions that I found on stackoverflow but that not work.

Upvotes: 0

Amin Shah Gilani
Amin Shah Gilani

Reputation: 9886

So, I had a similar problem; only I didn't have Devise or Docker. It was a simple form. Your question is missing a lot of contextual information like logs, so I can't tell if you have the same problem, but here's how I fixed mine.

I was getting InvalidAuthenticityToken errors for simple form submissions. Puzzling since it worked fine on Firefox, but would randomly fail on Chrome sometimes, and it always failed on Chrome on Android.

Diagnostics

I took a look at the log and found the following:

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):

The line that stands out is:

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

https://www.example.com indeed does not match http://www.example.com, the former has SSL. I was routing my app through Cloudflare, so I had SSL, but my app was expecting a request.base_url without SSL.

Solution

You need to force your app to use SSL. This is what I did to fix this; your exact steps may depend on your architecture. Because I was using Cloudflare, I had to perform these steps in this exact order otherwise, my app could have been knocked offline:

First: I configured SSL on my server. In this case, I was using Heroku, which can use Let's Encrypt to provision SSL automatically.

Second: I configured my app to force SSL by adding the following to production.rb

config.force_ssl = true

Third: Since I no longer needed an HTTP connection between my server and Cloudflare, I switched it to from Flexible to Full.

Upvotes: 1

Richard-Degenne
Richard-Degenne

Reputation: 2959

For what it's worth, I looked back at my code to find how I solved the issue.

I wasn't able to find a clean solution, so I worked around it by disabling the origin check :

# config/initializers/csrf_workaround.rb

Rails.application.config.action_controller.forgery_protection_origin_check = false

Of course, this introduces security vulnerabilities so be sure to post your own answer if you have a cleaner way to get this to work and/or have a real explanation for the question above.

Upvotes: 0

Related Questions