Where does Rails 4 store the authentication token for CSRF protection?
Asked Answered
W

1

6

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.

Woke answered 5/5, 2014 at 6:1 Comment(0)
S
12

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.

Sayette answered 5/5, 2014 at 6:19 Comment(6)
Thanks for actually looking at the project code. Surprisingly, it does accept legitimate requests, and now I am scratching my head trying to understand why.Woke
It's posting to post_transfer (the form action is hardcoded) rather than protected_post_transfer, so it should actually be vulnerable to CSRF. Are you sure it isn't?Sayette
Very good catch! It looks like the instructors screwed this up. I will point it at protected_post_transfer and see if it still accepts any requests.Woke
For what it's worth, this is why form_for and the Rails path helpers are nice - they manage all that kind of stuff for you, so those kinds of bugs don't crop up!Sayette
You were absolutely right. This code does not accept legitimate requests when protected_post_transfer is hit. That answers the question, albeit not the way I had expected.Woke
FWIW, the way to enable the protections is to use the form_tag helper, like <%=form_tag protected_post_transfer_path do %> ... <% end %> rather than <form>...</form>. If you switch to that you'll notice a hidden field containing the authenticity_token is written into the HTML.Sayette

© 2022 - 2024 — McMap. All rights reserved.