CSRF Protection Bypass in Ruby on Rails
Posted by michael February 08, 2011 @ 10:53 PM
There is a vulnerability in Ruby on Rails which could allow an attacker to circumvent the CSRF protection provided. This vulnerability has been assigned the CVE Identifier CVE-2011-0447.
- Versions Affected: 2.1.0 and above
- Not affected: Applications which don’t use the built in CSRF protection.
- Fixed Versions: 3.0.4, 2.3.11
Impact
Certain combinations of browser plugins and HTTP redirects can be used to trick the user’s browser into making cross-domain requests which include arbitrary HTTP headers specified by the attacker. An attacker can utilise this to spoof ajax and API requests and bypass the built in CSRF protection and successfully attack an application. All users running an affected release should upgrade or apply the patches immediately.
Releases
The 3.0.4 and 2.3.11 releases are available at the normal locations.
Upgrade Process
There are two major changes in this fix, the behaviour when CSRF protection fails has changed and the token will now be required for all non-GET requests.
After applying this patch failed CSRF requests will no longer generate HTTP 500 errors, instead the session will be reset. Users can override this behaviour by overriding handle_unverified_request in their own controllers.
Users must still take care that users cannot be auto logged in via non-session data. For example, an application using filters to implement ‘remember me’ functionality must either remove those cookies in their handle_unverified_request handlers or ensure that the remember me code is only executed on GET requests. A custom handler which removes the remember_me cookie would look like:
def handle_unverified_request
super # call the default behaviour which resets the session
cookies.delete(:remember_me) # remove the auto login cookie so the fraudulent request is rejected.
end
There are two steps to ensuring that your application sends the CSRF Token with every ajax request. Providing the token in a meta tag, then ensuring your javascript reads those values and provides them with each request. The first step involves you including the csrf_meta_tag helper somewhere in your application’s layout. Rails 3 applications likely already include this helper, however it has now been backported to the 2.3.x series. An example of its use would be something like this in application.html.erb:
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
In addition to altering the templates, an application’s javascript must be changed to send the token with Ajax requests. Rails 3 applications can just update their rails.js file using rake rails:update, 2.x applications which don’t use the built-in ajax view helpers will need to add a framework-specific snippet to their application.js. Examples of those snippets are available:
- prototype-snippet.js – Prototype script which includes the csrf token in every Ajax request
- jquery-snippet.js – JQuery script which includes the csrf token in every Ajax request.
Workarounds
There are no feasible workarounds for this vulnerability.
Patches
To aid users who aren’t able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format, 3-0-csrf.patch includes two changesets, the others consist of a single changeset.
- 2-3-csrf.patch – Patch for 2.3 series
- 3-0-csrf.patch – Patch for 3.0 series
Given the severity of the problem we are also providing backported fixes to the 2.2 and 2.1 series. There will be no gem releases for these versions but the stable branches in git will be updated.
- 2-2-csrf.patch – Patch for 2.2 series
- 2-1-csrf.patch – Patch for 2.1 series
Please note that only the 2.3.x and 3.0.x series are supported at present. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee continued security fixes indefinitely.
Credits
Thanks to Felix Gröbert of the Google Security Team for reporting the vulnerability to us and working with us to ensure that the fix didn’t introduce any new issues. Thanks also to the Shopify development team for their assistance in verifying the fix and the upgrade process. The original vulnerability has been reported to vendors by kuza55

Updating the rails javascript files is ‘rake rails:update’ not ‘rake rails:upgrade’
Thanks for all the work.
The rails.js update (rake rails:update for Rails 3) is Prototype-specific, is it not? Do you have a jQuery version of the necessary patch?
@anon: take a look at https://github.com/rails/jquery-ujs As you said it is Prototype specific. It updates only the js files to the ones shipped with that rails update.
Upgraded and all working so far. Thanks to all at the Rails team for the continued great work.
The jquery snippet doesn’t work on jquery 1.5 as far as I can tell. The ajaxSend event fires after the xhr state is set to 1, so setRequestHeader is ignored. I’ve been trying to figure out a sensible way of patching it, but might have to resort to just reordering jquery’s ajaxSend event to occur before the “state = jXHR.readyState = 1;” line…
Turns out that the setRequestHeader problem has already been fixed – https://github.com/jquery/jquery/commit/a2dbdc1f5438a857c2a9898bd36e4b2de685742e. Hopefully we’ll see that in jquery 1.5.1.
This works for me with jQuery 1.5 https://gist.github.com/818689
Also for jQuery <= 1.5, you can use xhr.setRequestHeader to set the token in ajaxSetup -> beforeSend
Thomas, thanks! Your fix works great.
Might be of help to others: Remember to output csrf_meta_tag BEFORE including jquery and the jquery fix.
I’ve just patched official rails.js with code that works in versions of jQuery including a workaround for 1.5: https://github.com/rails/jquery-ujs/blob/443de05/src/rails.js#L9-27
my bad. Got it working missed out something before. Thanks
When updating rails 2.3.* check your rack version. Rack 1.0.0 threw undefined method `set_cookie_header!’ for Rack::Utils:Module after upgrading to 2.3.11
Could you set this in ajaxSetup() as well, or are they executed differently?
jQuery.ajaxSetup({ ‘beforeSend’: function(xhr) { var token = $(“meta[name=’csrf-token’]”).attr(“content”); xhr.setRequestHeader(“X-CSRF-Token”, token); } })
I found this a helpful writeup:
http://jasoncodes.com/posts/rails-csrf-vulnerability
This is worth quoting…it’s in reference to doing your own cookie deletion in handle_unverified_request: “Please do not blindly upgrade to 2.3.11/3.0.4 without carefully testing your authentication system with POST requests lacking an authentication token. Without adding the above code we would effectively lose CSRF protection as the app will still see the authentication and process the request where there’s an invalid or missing authenticity token.”
Official rails.js for jQuery patched again, this time fixing an issue with IE: https://github.com/rails/jquery-ujs/blob/a284dd7/src/rails.js#L9-15
Warning: don’t use the `ajaxSetup` + `beforeSend` snipped provided by others (@jamieorc and the article in the above post). This in an incomplete solution.
I’ve never applied a patch to a gem. Can anyone give some advice?