MooTools and Rails CSRF Protection
June 25th, 2008
“Cross site request forgery” is also known as CSRF, XSRF or just request forgery (more at wikipedia and sans.org). It’s a method of attack toward web applications- Rails 2.0 introduced a defence and Rails 2.1 enabled that defence by default. Call form_for…
1 |
<% form_for @friend, :action => 'create' do |f| %> |
and Rails spits out more than just the form, it also generates a secret authenticity_token:
1 2 3 4 |
<form action="/friends" method="post"> <div style="margin:0;padding:0"> <input name="authenticity_token" type="hidden" value="e8c827c47577e013cc4c06a99cab63da95b71915" /> </div> |
AJAX submission of this particular form will include the authenticity token. So here’s the rub: what if the form is generated by something else?
1 |
new Element('form', { 'action': '/friends'}); |
Or what if there is no form?!
1 |
new Request.Json( ... ).post(); |
In these cases Rails would raise an ActionController::InvalidAuthenticityToken exception. To avoid an exception the CSRF check can be altogether disabled for a controller:
1 |
skip_before_filter :verify_authenticity_token
|
Of course this is a compromise between convenience and security. For those who want security and AJAXy goodness (well, with MooTools at least), there is a better way.
Using AuthenticityToken with MooTools
MooTools has a wonderful object oriented codebase. First Rails needs to pass the authenticity_token into Javascript, then we can use the inheritance from MooTools to pass the authenticity_token with every request.
The authenticity_token can be passed in the header of the Rails application layout. It’s just one line:
1 |
<%= javascript_tag "const AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> |
And now pass that token with every POST request (GET requests don’t check CSRF):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// Include authenticity_token in Request.JSON fires // // move the send function Request.prototype._send = Request.prototype.send Request.implement({ auth_token: function(){ return AUTH_TOKEN; }, // This is more verbose than is ideal, but I don't see a better place // to hook this functionality in send: function(options){ var type = $type(options); if (type == 'string' || type == 'element') options = {data: options}; var old = this.options; options = $extend({data: old.data, url: old.url, method: old.method}, options); switch ($type(options.data)){ case 'element': options.data = $(options.data).toQueryString(); break; case 'object': case 'hash': options.data = Hash.toQueryString(options.data); } // If this isn't a get request add the authenticity_token if (options.method != 'get' || options.method != 'GET') options.data = options.data+'&authenticity_token='+this.auth_token(); // Call the original send this._send(options) } }); |
Look at that, now every AJAX call from MooTools will attach our CSRF. All the ease of normal javascript, all the security of the native Rails defences.

Save This On Del.icio.us
this is some clever stuff, well done.
That’s awesome! Thanks for sharing this tips.
Nice.
Note that for my jQuery equivalent, someone commented that IE had issues with the
constkeyword, so I changed it intovarin the post. Haven’t tried in IE myself.I’ll check that out Henrik, thanks for the heads up!
You definitely have to change it from const to var for IE 6. Which I wish would just die already.
Hello.
Can you please help me with moving rails 2.1 to mootools?
The plugin mootools-on-rails doesn’t work here.