[ruby-on-rails] Understanding the Rails Authenticity Token

Minimal attack example that would be prevented: CSRF

On my website evil.com I convince you to submit the following form:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="to"      value="ciro"></p>
  <p><input type="hidden" name="ammount" value="100"></p>
  <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>

If you are logged into your bank through session cookies, then the cookies would be sent and the transfer would be made without you even knowing it.

That is were the CSRF token comes into play:

  • with the GET response that that returned the form, Rails sends a very long random hidden parameter
  • when the browser makes the POST request, it will send the parameter along, and the server will only accept it if it matches

So the form on an authentic browser would look like:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
  <p><input type="hidden" name="to"                 value="ciro"></p>
  <p><input type="hidden" name="ammount"            value="100"></p>
  <p><button type="submit">Send 100$ to Ciro.</button></p>
</form>

Thus, my attack would fail, since it was not sending the authenticity_token parameter, and there is no way I could have guessed it since it is a huge random number.

This prevention technique is called Synchronizer Token Pattern.

Same Origin Policy

But what if the attacker made two requests with JavaScript, one to read the token, and the second one to make the transfer?

The synchronizer token pattern alone is not enough to prevent that!

This is where the Same Origin Policy comes to the rescue, as I have explained at: https://security.stackexchange.com/questions/8264/why-is-the-same-origin-policy-so-important/72569#72569

How Rails sends the tokens

Covered at: Rails: How Does csrf_meta_tag Work?

Basically:

  • HTML helpers like form_tag add a hidden field to the form for you if it's not a GET form

  • AJAX is dealt with automatically by jquery-ujs, which reads the token from the meta elements added to your header by csrf_meta_tags (present in the default template), and adds it to any request made.

    uJS also tries to update the token in forms in outdated cached fragments.

Other prevention approaches