Where developers come to connect, share, build and be inspired.

16

Forms, CSRF authenticity token and fragment caching in Rails

4051 views

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

Comments

  • Blank-mugshot
    NARKOZ

    What if you want to do page/action caching?

  • Blank-mugshot
    einarj

    This just made my day, thanks!

Add a comment