views:

492

answers:

2

Hey,

please help a newbie in Rails :) I have protect_from_forgery call (which is given by default) with no attributes in my ApplicationController class.

Basically here's the code:

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery
  helper_method :current_user_session, :current_user
  filter_parameter_logging :password, :password_confirmation

What I assume it should do is: it should prevent any POST requests without correct authenticity_token. But when I send post request with jQuery like the one below, it works fine (there's update statement that is executed in the database)!

$.post($(this).attr("href"), { _method: "PUT", data: { test: true } });

I see in console that there's no authenticity_token among sent parameters, but request is still considered valid. Why is that?

UPD Found config setting in config/environments/development.rb

config.action_controller.consider_all_requests_local = true

Because of the DEV environment and local requests, these jQuery post requests were OK.

A: 

Silly question, perhaps: Are you sure you're subclassing ApplicationController? What's your route map look like? And what version of Rails (just for clarity)?

Did you verify that the call from jQuery is actually a POST and not a GET? (I know, it seems obvious). Rails will only perform the protection on non-GET requests.

Also, what is the content-type of the request that's going out. Rails will also only perform the protection, according to the docs, if it's an HTML/Javascript request.

orangechicken
And actually, looking at the source for verified_request? (which Rails uses to verify the authenticity token) it appears that if the request is xhr then it pays no attention to the auth token. *Maybe* jQuery sets the content-type to something that makes Rails think it's answering an xhr request and disregards the lack of an auth token?
orangechicken
Thanks for a response! Yes, I'm definitely inheriting ApplicationController (actually, I'm subclassing AdminController which is subclassing ApplicationController). And yes, this is POST/PUT request according to the "development.log" file. I also see all POST request parameters there and there is no authenticity_token. Shouldn't it raise an exception even in case of default jQuery contentType?!
Vitaly
Are you sure that if it's xhr request, then Rails doesn't pay any attention to the auth token? Isn't this a hole?
Vitaly
The code says that it will ignore the (lack of a) token if `request.xhr?` is true. If you `logger.debug request.xhr?`, what's it say? If it's true, then Rails is ignoring auth token and you'll have to futz with the contentType. It does seem like a security hole. To patch it up here you could deny any xhr requests.
orangechicken
Honestly, I don't think this is true: there're lots of discussions in the internet on how to add auth token to jquery post request (like this one - http://brandonaaron.net/blog/2009/02/24/jquery-rails-and-ajax). And without token it doesn't work. It must be something else, may be because of a local call as Paddy suggests.
Vitaly
What version of Rails are running? Did you verify if `request.xhr?` is true? The source I looked at was for v2.4, iirc, and that says it won't look for an auth token if it's an xhr request. This has changed in Rails 3, I believe.
orangechicken
Here's the link to the docs/source for verify_request? -- http://apidock.com/rails/ActionController/RequestForgeryProtection/verified_request%3F
orangechicken
+1  A: 

There is nothing wrong with your code as long as the request $.post($(this).attr("href"), { _method: "PUT", data: { test: true } }); is executed from within the app itself. If you had another app running elsewhere, say for example on localhost:3001, and you sent a post from there then it won't work. Infact if you are on firefox > 3.0 it has an early implementation of cross site xhr too. For example you can send a POST from any other site (but this works provided protect_from_forgery is turned off!). The reason why auth token is not necessary for xhr is that cross site xhr is disabled. So it is safe to use xhr without providing auth token. If you try from any where else other than your app, i am sure it will raise an exception asking for an auth token. Also you should have a crossdomain.xml defined to prevent access from outside sources.

Try doing this: curl -X -d url_endpoint_of_your_app. See if you get a 200 response code. If you do then there is something fishy.

Shripad K
I think it shouldn't be done from localhost even from another app. I will check, probably you're correct and this is regarded as local call without any additional security check.
Vitaly
even if you do from localhost it runs on different ports. Hence the apps run from different domains. Only solution is modifying crossdomain.xml
Shripad K
Awesome. I found a setting for DEV env that can be modified to change this behavior.
Vitaly
Could you also share what you found with all of us. :)
Shripad K
Sorry. Saw your edit now. :)
Shripad K