Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Browse files

Don't check authenticity tokens for any AJAX requests

  • Loading branch information...
commit 523f3ba8daf4968267eb4597bc88359a6337cf90 1 parent 60122e8
Ross Kaffenburger and Bryan Helmkamp authored lifo committed
2  actionpack/CHANGELOG
@@ -7,6 +7,8 @@
* Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") [DHH]
+* Don't check authenticity tokens for any AJAX requests [Ross Kaffenberger/Bryan Helmkamp]
* Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 [Gregg Pollack]
* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]
3  actionpack/lib/action_controller/request_forgery_protection.rb
@@ -81,12 +81,13 @@ def verify_authenticity_token
# Returns true or false if a request is verified. Checks:
- # * is the format restricted? By default, only HTML and AJAX requests are checked.
+ # * is the format restricted? By default, only HTML requests are checked.
# * is it a GET request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
def verified_request?
!protect_against_forgery? ||
request.method == :get ||
+ request.xhr? ||
!verifiable_request_format? ||
form_authenticity_token == params[request_forgery_protection_token]
11 actionpack/test/controller/request_forgery_protection_test.rb
@@ -151,14 +151,10 @@ def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_to
delete :index, :format => 'xml'
def test_should_allow_xhr_post_without_token
assert_nothing_raised { xhr :post, :index }
- def test_should_not_allow_xhr_post_with_html_without_token
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
- assert_raise(ActionController::InvalidAuthenticityToken) { xhr :post, :index }
- end
def test_should_allow_xhr_put_without_token
assert_nothing_raised { xhr :put, :index }
@@ -168,6 +164,11 @@ def test_should_allow_xhr_delete_without_token
assert_nothing_raised { xhr :delete, :index }
+ def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ assert_nothing_raised { xhr :post, :index }
+ end
def test_should_allow_post_with_token
post :index, :authenticity_token => @token
assert_response :success

4 comments on commit 523f3ba


Oh yeah? That's a big security change.


This has been discussed before, apparently it doesn't matter because of JavaScript's same origin policy.


True, it doesn't matter. XHR is detected using the X-Requested-With HTTP header. The type of attack that tokens prevent are caused by external sites making a fake request to your site by getting the user to click a link or submit a form, neither of which has the capacity to set headers. In fact XMLHttpRequest is the only client-side request mechanism that can set headers, and browser make sure it only talks to the current page's domain. Basically, if a request has custom headers set, it's either XHR from your own site or it's a request from another server-side program.



the presence of a custom header in a request is (as per the stanford CSRF paper) considered to be sufficient proof that it originated from you. This doesn't apply if you have a permissive crossdomain.xml file for flash, but if you have that you're screwed anyway.

Please sign in to comment.
Something went wrong with that request. Please try again.