Last Updated: December 07, 2016
·
10.37K
· bashir

Forms, CSRF authenticity token and fragment caching in Rails

Problem

You have a fragment cache like the following:

-cache ['v2', user.username, signed_in?, viewing_self?] do
  %li 
    = user.endorsements.count
    endorsements
  - user.works.each do |work|
    %li.work = work.title
    %li.endorse
      = form_tag(user_endorsements_path(@user)) do
        = hidden_field_tag :endorsed_work, work
        = link_to 'endorse', user_endorsements_path(user)

When the above haml code is compiled, rails inserts a unique csrf authenticity token (session based) in the form so when you submit it, it can rule out cross-site forgery attempts.

<form accept-charset="UTF-8" action="/users/6522/endorsements" method="post">

<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓">

<input name="authenticity_token" type="hidden" value="VY13wlC2rgGccbkxyvm7Z1WX4LKH+71vzIj+8Um0QO8="></div>
...
</form>

However, when your form is inside a cache fragment, as the example above demonstrates, the first time the html fragment is cached the authenticity token is part of it. Now if your sessions expires and you login again, you will be served the cache fragment that contains the original csrf authenticity token value and if you try to submit the form rails will end session(log you out) and return a

403 Forbidden

or

405 Method Not Allowed

error.

Solution

A simple solution is to put the following code in your application.js file so it replaces all authenticity tokens on a page with the default one in the header:

meta content="authenticity_token" name="csrf-param" />

meta content="VY13wlC2rgGccbkxyvm7Z1WX4LKH+71vzIj+8Um0QO8=" name="csrf-token" />

in application.js put(JQuery version):

$('input[name=authenticity_token]').val($('meta[name=csrf-token]').attr('content'))

now all old csrf tokens in the cache fragments will be replaced by the correct value from the current session

3 Responses
Add your response

What if you want to do page/action caching?

over 1 year ago ·

This just made my day, thanks!

over 1 year ago ·

NARKOZ: https://www.fastly.com/blog/caching-uncacheable-csrf-security discussed the techniques to set the correct CSRF token on a fully cached page.

over 1 year ago ·