6
votes

Inside one of my controllers, I write the following to protect certain pages from CSRF.

  protect_from_forgery :only => [:foo, :bar]

When I load the URL's which correspond to foo and bar, and I view the HTML, I do not see any hidden input fields or meta tags which contain any security tokens, as described here.

However, during testing, I did observe that CSRF is not effective against these pages, although it is effective against other pages in the same application which are not protected.

So where does Rails 4 store the security token which is used for verifying that the request came from the original page?

Note that I have already read through the Ruby On Rails Security Guide, and from the section on protect_from_forgery, it says

This will automatically include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected, the session will be reset.

The problem is that this security token appears to be missing from the forms on the pages with CSRF protection enabled, even though CSRF is indeed not effective against them.


Note, this code is from a class project, in which one of the objectives is to perform a clickjacking attack to bypass the CSRF project. The question I am asking here is orthogonal to the purpose of the assignment.

I am simply curious about exactly how Rails does CSRF.

After doing rails server in the directly, the relevant URL which I cannot find the security token for is http://localhost:3000/protected_transfer.

1

1 Answers

12
votes

The CSRF token is stored in the user's session (which is in a cookie by default, in Rails; encrypted cookie in Rails 4). It is additionally written into the page as both a <meta> tag (for use by Javascript libraries) via the csrf_meta_tags helper method, and in a hidden field in any forms generated by form_tag or form_for in the page.

Looking at this project, the reason the CSRF token doesn't appear is that the HTML is written with a literal <form> tag, rather than the form_for helper, which would include the CSRF token. Additionally, the csrf_meta_tags helper is not present in the layout, which is why the meta tag doesn't get written.

The form is hardcoded to post to <form action="post_transfer" method="post"> which should not be protected by CSRF protections, so this form should be CSRF-able, even though the view is marked as protect_from_forgery. The protected_post_transfer method isn't likely to accept even legitimate requests, since the authenticity token is never sent.

I suspect the instructors missed this, since the test would be to use the form legitimately (hitting the unverified endpoint, and letting it succeed), and then the student is instructed to attempt to CSRF against the protected endpoint (which will never pass muster anyway), so you end up testing two different things that produce the right results for the wrong reasons.