Rails 6 `protect_from_forgery with: :null_session` not working - ruby-on-rails

In an effort to learn React and using Rails together I am in the process of building a simple "Todo" app. Im using axios to make requests to my rails api within react components.
So far everything was working fine until I made my first POST request. Im pointing to the right place and sending fine params but saw I was getting Can't verify CSRF token authenticity.. Upon further googling it appears that adding:
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
Would fix my issue. But despite server restarts/etc... I still receive the same error when sending a POST request. Also adding prepend: true does not help either (as was suggested by another member).
Is there something im missing? This seems like a straightforward problem from what i've read but it doesn't seem to be working for me.
Im on Rails 6.0

if you just want the error to go away, use:
class ApplicationController < ActionController::Base
skip_forgery_protection
end
But if you would like to actually take advantage of the Rails forgery protection, then add an "X-CSRF-Token" header to your post request from React. The value for this header can be retrieved by javascript from the page's head element, in a meta tag included by Rails. For example (using jQuery):
var csrf_token_value = $('meta[name="csrf-token"]').attr('content')

Related

Devise InvalidAuthenticityToken

I am trying to create an authentication system with devise. When I try to login with a post request, without ajax, it gives me the "ActionController::InvalidAuthenticityToken" Error.
I've seen that this is supposed to be a common bug with devise. I've seen some usual approaches to it, which don't work for me:
To include an csrf token on the html: It is already included in the login site
Include a hackish solution like this: skip_before_action :verify_authenticity_token, if: -> { controller_name.include? 'sessions' }
. Unfortunately this doesn't change a thing. In the original solution I've seen it suggested to include it into Users::SessionsController. I can change whatever I like in this controller or even delete the whole controller and it still works the same.
This is supposed to be a problem with protecting from forgery according to other sources, but I can literally remove it from the application controller and I still have the same error.
class ApplicationController < ActionController::Base
# protect_from_forgery with: :exception
end
I believe that Devise checks the token independently from the rest of my app. This would also explain why it doesn't work even though there is a token at the sign in form. I have no clue what to do about it though.

Rails 4 - Endpoint for an API Callback

I recently upgraded from Rails 3.2 to Rails 4 and am now having trouble with one of my app's endpoints.
I'm trying to get an external service (Hellosign) to POST some JSON to my app after a user performs an action. For this to go through, my endpoint has to return a 200 HTTP code and a response body containing the following text: "Hello API Event Received"
This worked fine in Rails 3.2, but I'm having trouble doing this in Rails 4. I'd set up a route for this in config/routes.rb
post 'document' => 'requests#document'
My User model includes has_many :requests, and my Request model includes belongs_to :account. I'm using the latest version of Devise.
A stripped-down version of the Requests Controller is as follows:
class RequestsController < ApplicationController
before_filter :authenticate_user!, except: [:document]
skip_before_action :verify_authenticity_token, :only => [:document]
def document
render text: "Hello API Event Received"
#json = JSON.parse(params["json"])
end
end
I skip Devise authentication and verifying the authenticity token for the document action so that it can actually receive the POST from the API, which has its own authentication protocols.
THE PROBLEM: I'm getting a message from the API that the callback is failing, with an 'Access denied' error. When I check the production logs in Heroku, I get a ERROR invalid body size error message.
My best guess is that something has changed in Rails 4 with Devise and I can no longer use the before_filter and skip_before_action bits to allow the document action to serve as a public-facing endpoint.
Any help would be much appreciated!
Grrr, I found out the problem. The endpoint I specified to the API used http rather than https. Everything works swimmingly now after supplying https to the HelloSign API. However, the endpoint I'd supplied (with http) had worked with Rails 3.2 even after I'd added the SSL in my app...what in the Rails 4 upgrade made this fail? And how to make sense of the `ERROR invalid body size' message? Anyways, thanks to all who supplied suggestions!

Devise and CSRF

I am getting a CSRF Warning in my Rails logs intermittently. I think I have resolved the issue but would like some advice as to whether the solution is sound from a security point of view.
It's a Rails 4 app with Devise application with some Angular JS. Sometimes on a certain page the user session can time out and then we make an AJAX request (though I can see this in normal html requests too it just mainfests itself more regularly in JS) We get a 401 in the logs and can handle from that point. The problem is before that before that 401 we are getting the following message:
WARNING: Can't verify CSRF token authenticity
which is noisy and so I don't know if there is a real attack going on. They're logged out and their token is, I believe, no longer valid.
I believe this is because in Application Controller protect_from_forgery happens before my devise authenticate_user! filter. So:
class ApplicationController < ActionController::Base
protect_from_forgery
etc....
end
Happens before
class MyController < ApplicationController
before_filter :authenticate_user!
def new
end
end
If I change the above to prepend_before_action :authenticate_user! the CSRF message goes away. My question is there a (security) reason to not do this? I haven't seen other people do this so assume they must be getting this message too? It makes sense to me that Devise is one of the very first things that happens in our filter chain but don't see that as common practice.
Thanks in advance :)

How to make sure Rails API is secured from CSRF?

I've been developing Rails app with REST API for access from mobile application.
It works quite well. When user logs in from mobile application, he gets auth_token that he uses in his future requests to API. The issue is that API is also accessible from web by going to path /api/v1/... and because of this, it has to be protected from CSRF.
I have BaseApiController class which inherits from ApplicationController that has protect_from_forgery "enabled". Here's example:
class Api::V1::BaseApiController < ApplicationController
# ...
end
class ApplicationController < ActionController::Base
protect_from_forgery
# ...
end
Now, when I do non-GET requests to my API, with auth_token, my request gets completed successfully, but in the logs I can see the famous WARNING: Can't verify CSRF token authenticity. If I remove protect_from_forgery from my BaseApiController, I don't get any warnings (obviously), but then my API is vulnerable to CSRF attacks (I made a simple HTML form that successfully changes the data across domains when there's no protect_from_forgery).
My question is: How to assure my API stays secure, but also remove the warning when doing non-GET requests?
Here's one of the solutions I've come up with, but it looks more like a hack and executes one extra DB query:
class Api::V1::BaseApiController < ApplicationController
# ...
def verified_request?
super || User.where(authentication_token: params['auth_token']).count > 0
end
end
More details about the project: Rails 3.2.14, Devise, AngularJS. The project's source code can be found here.
You may see people suggest that CSRF is not an issue for API requests (there is no state to begin with, so what is there to hijack anyhow?), so some suggest the following to simply eliminate the warning:
skip_before_filter :verify_authenticity_token, :only => [:your_method]
However, there was some commentary that it is possible to commit CSRF with text/plain using various Flash and Java-based methods. I believe that was the reason for the security patch in rails a while back: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/
In any event, a good solution that actually checks for an authenticity token can be found here: WARNING: Can't verify CSRF token authenticity rails
It involves actually setting the header in your request.
Good luck!

Rails 3 gdata site wide youtube client

i want to uses youtube's api within rails.
I need a client which is able to access youtubes api application wide.
therefore i wrote the following application controller
require 'gdata'
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :auth
def auth
#client = GData::Client::YouTube.new
#client.clientlogin('usermail', 'password')
#client
end
end
i am able to use the client now in my controllers which extend ApplicationController.
thats working fine.
but its pretty slow.
is there a way to do the authentication once and using it application wide instead of suing the before_filter which is getting called every single time before i call a method?
best,
philip
This is a web page. Webpages are state-less. Thus you cannot preserve any state. Thus you cannot preserve your login across requests. Thus you have to auth every request.
An alternative would be to only run the before filter on certain controller actions. Right now it runs on every action, which my be not necessary.
Try:
before_filter :auth, :only=> my_action_name
(P.S. That might be the wrong syntax -- I'm confused 'cause rails changes so much -- just look it up)

Resources