ruby-on-railscsrfauthenticity-token

Rails authenticity_token on a form vs csrf token


On same page of a rails 4 app I have a

in the head:

<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="some_token" />

and below in the body:

<form action="/someaction" method="post">
<input name="utf8" type="hidden" value="&#x2713;" />
<input type="hidden" name="_method" value="patch" />
<input type="hidden" name="authenticity_token" value="another_token" />

The csrf token is need for js calls. But why is the form token different from the csrf token? Which of the two tokens is used on form submission?


Solution

  • I've made some researches to answer your question, and here are results.

    First of all, let's look at this part:

    <meta name="csrf-param" content="authenticity_token" />
    <meta name="csrf-token" content="some_token" />
    

    This part is generated by method csrf_meta_tags. From the source code we can see that:

    1. "content" attribute value of <meta name="csrf-param" /> is taken from request_forgery_protection_token, and, by default, is :authenticity_token.

    2. "content" attribute value of <meta name="csrf-token" /> is taken from form_authenticity_token method, where the token is either taken from session, or generated.

    Now let's check out this part:

    <input type="hidden" name="authenticity_token" value="another_token" />
    

    From the source we can see that:

    1. This hidden input is returned by extra_tags_for_form method.
    2. Inside extra_tags_for_form invokes token_tag method.
    3. token_tag method takes the token as the argument.
    4. token argument for token_tag is previously extracted from options argument of form_tag method in html_options_for_form method.

    So if you didn't manually set authenticity_token param in options to your custom token and didn't meet the conditions that lead to setting token value to false (will be mentioned below), token_tag method will receive nil and invoke the same form_authenticity_token method that is used for the <meta name="csrf-token" /> tag creating. By the way, for filling name attribute of input it also uses request_forgery_protection_token, that is used when <meta name="csrf-param" /> tag generation occurs.

    And because this all occurs during the same request, invocation of form_authenticity_token method should return the same result in both cases.

    Which of the two tokens is used on form submission?

    On form submission will be used the token from hidden input.

    Token from <meta /> also can be used, but only if all of below conditions (that make token argument of token_tag method be set to false) will be met:

    1. :remote => true should be passed in options of form_tag.
    2. embed_authenticity_token_in_remote_forms config is set to false.
    3. authenticity_token wasn't passed in options.

    But why is the form token different from the csrf token?

    As for this question, maybe this problem occurs because of caching. Or, probably, if you use Turbolinks gem, it might cause this issue (you can check this if you totally refresh the page and compare tokens once again). For more info about problem with Turbolinks, check this question.