Rails app can't verify CSRF token on chrome only
Asked Answered
J

3

13

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.

Jacksmelt answered 29/5, 2017 at 14:19 Comment(8)
Does the browser has cookies enabled?Peugia
Yes, it does have cookies.Jacksmelt
I'm having the same issue right now, did you manage to solve this?Meal
It's been a while, but I remember that this Github issue helped me a lot, though. Hope it helps. Also, fell free to answer the question if you come across a solution. :)Jacksmelt
I'm also having a similar issue, and not seeming to get anywhere with the Github thread. @RichouHunter, did you manage to solve this?Estray
@MartinNyaga: Sorry, same as before. It was a long time ago, and I can't find how I solved the issue or worked around it. :(Jacksmelt
Depending on your Rails version, you may have to set the local: true flag in your form_for to force forms to submit without ajax. I believe the issue is the ajax submission.Blanka
I had this exact issue with Rails 5.x and Webkit based browsers (Firefox was fine). It ended up being Cloudflare having SSL set to Flexible instead of Full for my domain. Setting it to Full fixed the issue.Skin
J
0

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.

Jacksmelt answered 28/5, 2018 at 14:22 Comment(0)
I
1

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.

Impious answered 26/9, 2019 at 23:25 Comment(2)
Hi, and thank you for your answer. Unfortunately, I'm afraid that the problem you faced is not the same as described in the question, because my logs never mentioned the "HTTP Origin header didn't match request.base_url" part.Jacksmelt
I understand @Richard-Degenne. Could you try posting your CORS implementation and the params your controller processes? Without sensitive information, of course.Impious
J
0

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.

Jacksmelt answered 28/5, 2018 at 14:22 Comment(0)
H
0

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.

Highwrought answered 7/9, 2021 at 3:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.