Rails 2.0 (and later versions) have a feature called protect_from_forgery. This feature goes a long way in preventing CSRF attacks and, in applications that use RESTful resources, practically stops them altogether. Originally by Rick Olson (AKA technoweenie, the author of many popular Rails plugins) in the form of the CsrfKiller plugin, the functionality was considered so essential it was folded into the Rails project and enabled by default on all new applications generated by Rails.
Below is a form generated by the form helpers (such as form_for or form_tag) without forgery protection enabled. Since it has no hidden ID field, it's not protected from forged POST requests. Thus, the form and the action it submits to are vulnerable to CSRF attacks.
<form action="/posts/create" class="new_post" id="new_post" method="post"> <input id="post_title" name="post[title]" size="30" type="text" /><br /> <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea><br /> <input name="commit" type="submit" value="Create" /> </form>
A Hidden ID Field
Once forgery protection is enabled, the form now has a hidden ID field. The protect_from_forgery method adds a before_filter called verify_authenticity_token to all actions. It will compare this ID field against an ID stored in the session variable. If they differ, the action will not be executed. Here's the form with the hidden ID field after protect_from_forgery has been enabled.
<form action="/posts/create" class="new_post" id="new_post" method="post"> <input name="authenticity_token" type="hidden" value="01048766ce9a8a001a85386a9502f64e64991ea8" /> <input id="post_title" name="post[title]" size="30" type="text" /><br /> <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea><br /> <input name="commit" type="submit" value="Create" /> </form>
Enable Forgery Protection Pre-Rails 2.0
To enable forgery protection in applications generated before Rails 2.0, add the following protect_from_forgery line to your application controller. (In projects generated with Rails 2.0 or later, this line is already in place.) Below is the example application controller with theprotect_from_forgery line added. If you are adding the line, be sure to replace the secret parameter with a random string other than the one listed below.
class ApplicationController < ActionController::Base include AuthenticatedSystem helper :all # include all helpers, all the time # See ActionController::RequestForgeryProtection for details # Uncomment the :secret if you're not using the cookie session store protect_from_forgery # :secret => 'b09ddc8519180659f80e8bb321b7fd26' end
Storing Session Variables
There are two ways Rails can store session variables. A cookie-based session store will store the session variables encrypted within the cookie itself. This is the default session store on applications generated with Rails 2.0 or later. It doesn't require any storage space in the database or in the /tmp directory. This was done partially for performance reasons; benchmarks showed it was faster to decrypt session variables from a cookie than to search through large database tables or directories for the same information. It also helps server clusters, because they don't have to share the same session store. If you're using a cookie-based session store, you can leave the protect_from_forgery line as is. If you're using a server-based session store, however, you need to uncomment the :secret argument.
Once protect_from_forgery is in place, all forms generated by the form helpers will be protected when submitting to actions using the POST method. However, the example application does not pay attention to idempotence and the database can be altered via GET requests as well. The protect_from_forgery fea=ture does not protect against this.