Permalink
Browse files

Don't check authenticity tokens for any AJAX requests

  • Loading branch information...
Ross Kaffenburger and Bryan Helmkamp authored and wycats committed Mar 4, 2009
1 parent 3c11876 commit 256b0ee8e3c1610967dfc89f864e24b98ed3c236
View
@@ -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]
@@ -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]
end
@@ -151,14 +151,10 @@ def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_to
delete :index, :format => 'xml'
end
end
-
+
def test_should_allow_xhr_post_without_token
assert_nothing_raised { xhr :post, :index }
end
- 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 }
end
+ 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

36 comments on commit 256b0ee

@Manfred

This comment has been minimized.

Show comment
Hide comment
@Manfred

Manfred Apr 16, 2009

Contributor

Yehuda, can you explain this commit? Is this associated with a Lighthouse ticket somehow?

Contributor

Manfred replied Apr 16, 2009

Yehuda, can you explain this commit? Is this associated with a Lighthouse ticket somehow?

@nate

This comment has been minimized.

Show comment
Hide comment
@nate

nate Apr 16, 2009

Why was this removed?

nate replied Apr 16, 2009

Why was this removed?

@jasonroelofs

This comment has been minimized.

Show comment
Hide comment
@jasonroelofs

jasonroelofs Apr 16, 2009

Um, wasn’t half the point of Auth Tokens security against rogue AJAX requests? What is the point of this commit?

Um, wasn’t half the point of Auth Tokens security against rogue AJAX requests? What is the point of this commit?

@norbert

This comment has been minimized.

Show comment
Hide comment
@norbert

norbert Apr 16, 2009

Contributor

Yeah, this seems odd.

Contributor

norbert replied Apr 16, 2009

Yeah, this seems odd.

@wycats

This comment has been minimized.

Show comment
Hide comment
@wycats

wycats Apr 16, 2009

Member

@jameskilton rogue Ajax requests cannot produce CSRF attacks, so this was just an annoyance to people hand-writing JS.

Member

wycats replied Apr 16, 2009

@jameskilton rogue Ajax requests cannot produce CSRF attacks, so this was just an annoyance to people hand-writing JS.

@carllerche

This comment has been minimized.

Show comment
Hide comment
@carllerche

carllerche Apr 16, 2009

Contributor

jameskilton: If you can make a rogue AJAX request, you have bigger problems than CSRF attacks ;)

Contributor

carllerche replied Apr 16, 2009

jameskilton: If you can make a rogue AJAX request, you have bigger problems than CSRF attacks ;)

@labria

This comment has been minimized.

Show comment
Hide comment
@labria

labria Apr 17, 2009

Contributor

At last, no dumb tokens to generate by hand =)

Contributor

labria replied Apr 17, 2009

At last, no dumb tokens to generate by hand =)

@wycats

This comment has been minimized.

Show comment
Hide comment
@wycats

wycats Apr 17, 2009

Member

@labria at last… a positive comment on this change! W00H00!

Member

wycats replied Apr 17, 2009

@labria at last… a positive comment on this change! W00H00!

@lucashungaro

This comment has been minimized.

Show comment
Hide comment
@lucashungaro

lucashungaro Apr 17, 2009

Contributor

Very nice indeed. :)

Contributor

lucashungaro replied Apr 17, 2009

Very nice indeed. :)

@NZKoz

This comment has been minimized.

Show comment
Hide comment
@NZKoz

NZKoz Apr 17, 2009

Member

The same original policy protects ajax requests. you can’t make browsers submit forms or make requests with custom headers.

This should be safe until browsers drop the same origin policy, which is (one hopes) forever.

Member

NZKoz replied Apr 17, 2009

The same original policy protects ajax requests. you can’t make browsers submit forms or make requests with custom headers.

This should be safe until browsers drop the same origin policy, which is (one hopes) forever.

@adrianpacala

This comment has been minimized.

Show comment
Hide comment
@adrianpacala

adrianpacala Apr 17, 2009

Contributor

Why thank you kind sir. No more ugly inline JS only for that one pesky line of auth token.

Contributor

adrianpacala replied Apr 17, 2009

Why thank you kind sir. No more ugly inline JS only for that one pesky line of auth token.

@gbuesing

This comment has been minimized.

Show comment
Hide comment
@gbuesing

gbuesing Apr 17, 2009

Contributor

This is also a big win for cacheability, because we can remove user-specific authenticity tokens from ajaxified links and forms that don’t need to support a non-ajax fallthrough.

Very nice!

Contributor

gbuesing replied Apr 17, 2009

This is also a big win for cacheability, because we can remove user-specific authenticity tokens from ajaxified links and forms that don’t need to support a non-ajax fallthrough.

Very nice!

@bit-forge

This comment has been minimized.

Show comment
Hide comment
@bit-forge

bit-forge Apr 17, 2009

Excellent! This was a pain when implementing unobtrusive JS…

Excellent! This was a pain when implementing unobtrusive JS…

@tilsammans

This comment has been minimized.

Show comment
Hide comment
@tilsammans

tilsammans Apr 17, 2009

Contributor

I don’t understand this commit. What’s stopping an attacker who is compiling a browser herself and making different-origin a reality? Now the security of Rails apps is tied to ‘hopes’ of third parties doing the right thing?

Contributor

tilsammans replied Apr 17, 2009

I don’t understand this commit. What’s stopping an attacker who is compiling a browser herself and making different-origin a reality? Now the security of Rails apps is tied to ‘hopes’ of third parties doing the right thing?

@samleb

This comment has been minimized.

Show comment
Hide comment
@samleb

samleb Apr 17, 2009

Contributor

@tilsammans: I believe CSRF protection is only useful to prevent users from doing cross-site requests without knowing it (i.e. submitting a form whose

action
is on another site where he’s authenticated).
If one compiles its own browser making different-origin a reality as you suggested, he will be the only one in danger. Am I missing something here ?
Contributor

samleb replied Apr 17, 2009

@tilsammans: I believe CSRF protection is only useful to prevent users from doing cross-site requests without knowing it (i.e. submitting a form whose

action
is on another site where he’s authenticated).
If one compiles its own browser making different-origin a reality as you suggested, he will be the only one in danger. Am I missing something here ?
@samleb

This comment has been minimized.

Show comment
Hide comment
@samleb

samleb Apr 17, 2009

Contributor

Sorry about the preformatted tag above… having previews or comment edition would be such a nice feature ;)

Contributor

samleb replied Apr 17, 2009

Sorry about the preformatted tag above… having previews or comment edition would be such a nice feature ;)

@RStankov

This comment has been minimized.

Show comment
Hide comment
@RStankov

RStankov Apr 17, 2009

Contributor

Really great addition, will save me a lot unnecessary hacking :)

@tilsammans
As far as I know protected_from_forgery, is for securing the frontend users, from other users/sites/scripts, who tried to pretend to be the frontend user. I mean that a person who is compiling a browser himself, for example, could only take his session data, witch is his. This is the same as that I could change the html with FireBug and fire some forms, run ajax queries, and other things from every site.

Contributor

RStankov replied Apr 17, 2009

Really great addition, will save me a lot unnecessary hacking :)

@tilsammans
As far as I know protected_from_forgery, is for securing the frontend users, from other users/sites/scripts, who tried to pretend to be the frontend user. I mean that a person who is compiling a browser himself, for example, could only take his session data, witch is his. This is the same as that I could change the html with FireBug and fire some forms, run ajax queries, and other things from every site.

@milodurden

This comment has been minimized.

Show comment
Hide comment
@milodurden

milodurden Apr 18, 2009

So what happens when someone posts a CSRF request with the header ‘X-Requested-With’ set to ‘XMLHttpRequest’ ?

So what happens when someone posts a CSRF request with the header ‘X-Requested-With’ set to ‘XMLHttpRequest’ ?

@NZKoz

This comment has been minimized.

Show comment
Hide comment
@NZKoz

NZKoz Apr 18, 2009

Member

@mildodurden: You can’t do that with browsers as they exist currently. If you can prove otherwise, please let us know and we can revert this.

Member

NZKoz replied Apr 18, 2009

@mildodurden: You can’t do that with browsers as they exist currently. If you can prove otherwise, please let us know and we can revert this.

@NZKoz

This comment has been minimized.

Show comment
Hide comment
@NZKoz

NZKoz Apr 18, 2009

Member

@tilsammans: If the attacker is executing custom browsers on the user’s computer, there’s nothing we can do about it. They could also be running keyloggers, screenshotters, botnets etc.

This is for protecting against CSRF attacks, not a silver bullet for all possible attacks ever inventable.

Member

NZKoz replied Apr 18, 2009

@tilsammans: If the attacker is executing custom browsers on the user’s computer, there’s nothing we can do about it. They could also be running keyloggers, screenshotters, botnets etc.

This is for protecting against CSRF attacks, not a silver bullet for all possible attacks ever inventable.

@wycats

This comment has been minimized.

Show comment
Hide comment
@wycats

wycats Apr 18, 2009

Member

For further details on CSRF, check out this great Stanford paper:

XMLHttpRequest’s popularity has increased recently with more sites implementing AJAX interfaces. Sites can defend against CSRF by setting a custom header via XMLHttpRequest and validating that the header is present before processing state-modifying requests. Although effective, this defense requires sites to make all state-modifying requests via XMLHttpRequest, a requirement that prevents many natural site designs.
Member

wycats replied Apr 18, 2009

For further details on CSRF, check out this great Stanford paper:

XMLHttpRequest’s popularity has increased recently with more sites implementing AJAX interfaces. Sites can defend against CSRF by setting a custom header via XMLHttpRequest and validating that the header is present before processing state-modifying requests. Although effective, this defense requires sites to make all state-modifying requests via XMLHttpRequest, a requirement that prevents many natural site designs.
@arthurgeek

This comment has been minimized.

Show comment
Hide comment
@arthurgeek

arthurgeek Apr 19, 2009

Contributor

Any chance to see this change on 2.3 branch? :)

Contributor

arthurgeek replied Apr 19, 2009

Any chance to see this change on 2.3 branch? :)

@arthurschreiber

This comment has been minimized.

Show comment
Hide comment
@arthurschreiber

arthurschreiber Apr 19, 2009

+1 for a backport! :)

+1 for a backport! :)

@josevalim

This comment has been minimized.

Show comment
Hide comment
@josevalim

josevalim Apr 19, 2009

Contributor

+1 for a backport!

Contributor

josevalim replied Apr 19, 2009

+1 for a backport!

@samgranieri

This comment has been minimized.

Show comment
Hide comment
@samgranieri

samgranieri Apr 20, 2009

Contributor

+1 for a backport

Contributor

samgranieri replied Apr 20, 2009

+1 for a backport

@nate

This comment has been minimized.

Show comment
Hide comment
@nate

nate Apr 20, 2009

You could backport it in a backwards compatible way. Check it if it’s included, otherwise ignore it.

nate replied Apr 20, 2009

You could backport it in a backwards compatible way. Check it if it’s included, otherwise ignore it.

@ultrasaurus

This comment has been minimized.

Show comment
Hide comment
@ultrasaurus

ultrasaurus Apr 20, 2009

+1 for for 2.3

+1 for for 2.3

@ultrasaurus

This comment has been minimized.

Show comment
Hide comment
@ultrasaurus

ultrasaurus Apr 20, 2009

Just saw binary42’s response. There reason to backport it, is that new code is still being written for Rails and this is a stumbling block when you want to make requests from Ajax, desktop client, or Flash. I don’t see this as a policy change, so much as a bug fix. (But I’m new to Rails, maybe I’m missing something)

Just saw binary42’s response. There reason to backport it, is that new code is still being written for Rails and this is a stumbling block when you want to make requests from Ajax, desktop client, or Flash. I don’t see this as a policy change, so much as a bug fix. (But I’m new to Rails, maybe I’m missing something)

@paulrosania

This comment has been minimized.

Show comment
Hide comment
@paulrosania

paulrosania Apr 20, 2009

Contributor

What about local XSS attacks? With unescaped output, something akin to the MySpace worm is enabled by this change.

Granted, Rails coders always remember to escape output! :) But was the intent to remove a layer of defensive programming?

Contributor

paulrosania replied Apr 20, 2009

What about local XSS attacks? With unescaped output, something akin to the MySpace worm is enabled by this change.

Granted, Rails coders always remember to escape output! :) But was the intent to remove a layer of defensive programming?

@ultrasaurus

This comment has been minimized.

Show comment
Hide comment
@ultrasaurus

ultrasaurus Apr 20, 2009

-1 for 2.3 (revised vote)

@binary42 after thinking about this more this morning, I realized that changing the behavior in a dot release is probably unwise. If there is an existing app depending on this behavior with an open cross-domain.xml and using cookie-based authentication, then a malicious flash app hosted on another site could make requests on behalf of a user without them being aware of it.

In terms of the “5 minute” fix… I’m still struggling to find the right few lines of code to write. I thought it work would to add the following to my controller:

def verify_authenticity_token
  request.xhr?      ||
  verified_request? || 
  raise(ActionController::InvalidAuthenticityToken)
end

but I still get the exception. I’m sure I’m missing something basic. Is the code change I need to make documented somewhere?

Thanks!

-1 for 2.3 (revised vote)

@binary42 after thinking about this more this morning, I realized that changing the behavior in a dot release is probably unwise. If there is an existing app depending on this behavior with an open cross-domain.xml and using cookie-based authentication, then a malicious flash app hosted on another site could make requests on behalf of a user without them being aware of it.

In terms of the “5 minute” fix… I’m still struggling to find the right few lines of code to write. I thought it work would to add the following to my controller:

def verify_authenticity_token
  request.xhr?      ||
  verified_request? || 
  raise(ActionController::InvalidAuthenticityToken)
end

but I still get the exception. I’m sure I’m missing something basic. Is the code change I need to make documented somewhere?

Thanks!

@gbuesing

This comment has been minimized.

Show comment
Hide comment
@gbuesing

gbuesing Apr 20, 2009

Contributor

@ultrasaurus for a quick 2.3 patch, check out this gist: http://gist.github.com/98660 . You can put this an initializer and it should work

Contributor

gbuesing replied Apr 20, 2009

@ultrasaurus for a quick 2.3 patch, check out this gist: http://gist.github.com/98660 . You can put this an initializer and it should work

@arthurschreiber

This comment has been minimized.

Show comment
Hide comment
@arthurschreiber

arthurschreiber Apr 22, 2009

@paulrosania: If your users are able to execute JavaScript through a XSS, this authenticity token won't be able to prevent CSRFs at all. It would be a snap to fetch the authenticity token from a page which has a form on it...

@everyone else who wants to see this in 2.3: http://gist.github.com/99635 <- a simple initializer like the one of gbuesing, but not using class eval.

@paulrosania: If your users are able to execute JavaScript through a XSS, this authenticity token won't be able to prevent CSRFs at all. It would be a snap to fetch the authenticity token from a page which has a form on it...

@everyone else who wants to see this in 2.3: http://gist.github.com/99635 <- a simple initializer like the one of gbuesing, but not using class eval.

@NZKoz

This comment has been minimized.

Show comment
Hide comment
@NZKoz

NZKoz Apr 22, 2009

Member

@paulrosania: In fact the twitter worm did exactly what NoKarma is describing. XSS is the stack-smashing of web attacks, if you're attacked you're done. Nothing can save you

Member

NZKoz replied Apr 22, 2009

@paulrosania: In fact the twitter worm did exactly what NoKarma is describing. XSS is the stack-smashing of web attacks, if you're attacked you're done. Nothing can save you

@vrybas

This comment has been minimized.

Show comment
Hide comment
@vrybas

vrybas May 4, 2009

Contributor

@ultrasaurus:
skip_before_filter :verify_authenticity_token, :only => [:aircrafts_by_manufacturer]
?

Contributor

vrybas replied May 4, 2009

@ultrasaurus:
skip_before_filter :verify_authenticity_token, :only => [:aircrafts_by_manufacturer]
?

@NZKoz

This comment has been minimized.

Show comment
Hide comment
@NZKoz

NZKoz May 4, 2009

Member

@vrybas: instead of explicitly skipping the filter you should do:

protect_from_forgery :except=>[:aircrafts_by_manufacturer]

I'm also reasonably sure that you could do something along the lines of

protect_from_forgery :if=>:some_method_which_returns_true_when_ajax_or_whatever

Member

NZKoz replied May 4, 2009

@vrybas: instead of explicitly skipping the filter you should do:

protect_from_forgery :except=>[:aircrafts_by_manufacturer]

I'm also reasonably sure that you could do something along the lines of

protect_from_forgery :if=>:some_method_which_returns_true_when_ajax_or_whatever

@jdwyah

This comment has been minimized.

Show comment
Hide comment
@jdwyah

jdwyah Jul 17, 2009

Isn't it pretty easy to add authenticity_tokens to all ajax requests? ie these 7 lines of code? http://gist.github.com/149110

Isn't it pretty easy to add authenticity_tokens to all ajax requests? ie these 7 lines of code? http://gist.github.com/149110

Please sign in to comment.