Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate form authenticity tokens when more than 1 form on the page #32930

Closed
mockdeep opened this issue May 18, 2018 · 21 comments
Closed

Duplicate form authenticity tokens when more than 1 form on the page #32930

mockdeep opened this issue May 18, 2018 · 21 comments
Labels

Comments

@mockdeep
Copy link
Contributor

Steps to reproduce

Add 2 forms to the same page with per_form_csrf_tokens = true and csrf_meta_tags in the header.

Expected behavior

The CSRF token in the head and in each form will all be distinct.

Actual behavior

All 3 CSRF tokens will be the same. However, when there is only 1 form on the page, the token in the head will be distinct from the one in the head, and if we remove csrf_meta_tags from the head, all form tokens will be unique.

System configuration

Rails version: 5.2.0

Ruby version: 2.4.1

@gwincr11
Copy link
Contributor

👋 Thanks for submitting this @mockdeep a few questions.

  1. Can you provide a sample application showing the issue?
  2. I put one together myself and was not able to reproduce, I am curious if it is a particular form tag call. What happens if you manually add the authenticity token fields to your form with <%= hidden_field_tag :authenticity_token, form_authenticity_token -%>?

Thanks!

@mockdeep
Copy link
Contributor Author

Hey @gwincr11, here's a repro in a new rails app. See users/_form.html.erb for my changes. I didn't change any configs or anything. All the default authenticity tokens are the same, but when I do form_authenticity_token it ends up being different (I labeled it :authenticity_token_2):

untitled

One thing that was interesting is that in this app the tokens duplicate even when there's only one form on the page, but that's not what I observed in another application I tried this on.

@gwincr11
Copy link
Contributor

🤔So I pulled down your repo and set everything up, when I inspect the authenticity tokens in the chrome web inspector I show the duplication just as you do.

However when I view source in the browser I do not see the issue.
screen shot 2018-05-20 at 8 32 13 pm

This is super weird. The same thing occurs in Safari and Firefox.

So I printed them out with Javascript and JS shows them as each being the same.
screen shot 2018-05-20 at 8 43 44 pm

I am going to keep digging into this but it is super weird so far.

@gwincr11
Copy link
Contributor

Interesting if I change the name on the input element with the authenticity_token2 as the name then that also gets the same token.

@gwincr11
Copy link
Contributor

I think this maybe a browser implementation issue. When I curl the page into a file I get the proper html:

<!DOCTYPE html>
<html>
  <head>
    <title>CsrfTest</title>
    <meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="ZjG5Kw0ty0odnmjnF4N9lIvQJUkMBs9BaIqLgC4SJxgneglcocWl0GN9XqtqyXUaKhIuLLkjQ5C2ZRH3rZdW7g==" />
    

    <link rel="stylesheet" media="all" href="/assets/scaffolds.self-e607afa58a866f84f058a2ae3883b67629ebb713b2cc1dd8ee17a00ca4eeb50f.css?body=1" data-turbolinks-track="reload" />
<link rel="stylesheet" media="all" href="/assets/users.self-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css?body=1" data-turbolinks-track="reload" />
<link rel="stylesheet" media="all" href="/assets/application.self-f0d704deea029cf000697e2c0181ec173a1b474645466ed843eb5ee7bb215794.css?body=1" data-turbolinks-track="reload" />
    <script src="/assets/rails-ujs.self-551fbd47b981dacbb84a270f9123074caf39eb72aaf6f478ab597c6f81435e4b.js?body=1" data-turbolinks-track="reload"></script>
<script src="/assets/activestorage.self-6e8d967adecc8b2a7259df0f51ef5b6f171c33267c7d149a474beccd90c68697.js?body=1" data-turbolinks-track="reload"></script>
<script src="/assets/turbolinks.self-2db6ec539b9190f75e1d477b305df53d12904d5cafdd47c7ffd91ba25cbec128.js?body=1" data-turbolinks-track="reload"></script>
<script src="/assets/action_cable.self-69fddfcddf4fdef9828648f9330d6ce108b93b82b0b8d3affffc59a114853451.js?body=1" data-turbolinks-track="reload"></script>
<script src="/assets/cable.self-8484513823f404ed0c0f039f75243bfdede7af7919dda65f2e66391252443ce9.js?body=1" data-turbolinks-track="reload"></script>
<script src="/assets/users.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" data-turbolinks-track="reload"></script>
<script src="/assets/application.self-66347cf0a4cb1f26f76868b4697a9eee457c8c3a6da80c6fdd76ff77e911715e.js?body=1" data-turbolinks-track="reload"></script>
  </head>

  <body>
    <h1>New User</h1>

<form action="/users/new" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="Gjs8/hM3vx8ahD7ccs3roJjhlnm+TPp9Re5ZKenBX59xFXBpFa7MnV/GxKTfOBCUsraGpZSZt8hGr148D6eUVw==" />
  <input type="text" name="nada" id="nada" />
</form>
<form action="/users" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="YnWkCeq92Lim6cViqAQv51DAXA9SA2tGM94GBk8Z0Q0Am74CY8TntXZ+Dc9Mm7Qx1oY7bQsYSU0gudNGEsDR4A==" />
  <input type="hidden" name="authenticity_token" id="authenticity_token" value="3RSRR61Mchd+b+lJGQHfG+SZVTQyJPmPws7HfZrvczecXyEwAaQcjQCM3wVkS9eVRVteUYcBdV4cIV0KGWoCwQ==" />
  <div class="field">
    <label for="user_name">Name</label>
    <input type="text" name="user[name]" id="user_name" />
  </div>

  <div class="field">
    <label for="user_email">Email</label>
    <input type="text" name="user[email]" id="user_email" />
  </div>

  <div class="actions">
    <input type="submit" name="commit" value="Create User" data-disable-with="Create User" />
  </div>
</form>5.2.0


<a href="/users">Back</a>

  </body>
</html>

@mockdeep
Copy link
Contributor Author

Oh! I see. It's because it has id="authenticity_token". You're only allowed to have 1 id on the page.

@mockdeep
Copy link
Contributor Author

And name is duplicated, too, maybe it's that.

@gwincr11
Copy link
Contributor

You should be able to have two forms that have the same name for an input element, that is valid HTML and when I put it into the w3c html validator it doesn't say it is invalid.

@upinetree
Copy link

Same issue here 🙋‍♂️
I think it is caused by rails-ujs's refreshCSRFTokens() behaviour.

refreshCSRFTokens() overwrites all inputs that has name=authenticity_token when DOMContentLoaded fired.
So saving page with curl does not execute JavaScript and result the proper html. (and disabling JavaScript on the browser could also reproduce a same result)

In addition, this refreshCSRFToken does not affect after clicking a link on turbolinks environment, because it is on DOMContentLoaded.

@rails-bot rails-bot bot added the stale label Aug 27, 2018
@rails-bot
Copy link

rails-bot bot commented Aug 27, 2018

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-2-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@mockdeep
Copy link
Contributor Author

This is still an issue. I updated my repro to Rails 5.2.1, and also tested it against master, both with the same results as before.

@rails-bot rails-bot bot removed the stale label Aug 27, 2018
@rails-bot
Copy link

rails-bot bot commented Nov 25, 2018

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-2-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot added the stale label Nov 25, 2018
@mockdeep
Copy link
Contributor Author

Still an issue.

@rails-bot rails-bot bot removed the stale label Nov 25, 2018
@rails-bot
Copy link

rails-bot bot commented Feb 23, 2019

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-2-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot added the stale label Feb 23, 2019
@rails-bot rails-bot bot closed this as completed Mar 2, 2019
@kramer33
Copy link

Same issue on Rails 5.2.3

@reddyonrails
Copy link

reddyonrails commented Jun 10, 2019

@kramer33
Assuming that you are using Turbolinks, this worked for me.

document.addEventListener(Turbolinks.EVENTS.CHANGE, function() {
    $.rails.refreshCSRFTokens();
});

@kramer33
Copy link

@reddyonrails

Yes I'm using Turbolinks, but I think calling $.rails.refreshCSRFTokens() on every Turbolinks CHANGE event will not resolve this issue. It still makes all the CSRF tokens the same value, that's exactly the issue here. The expected behavior is

The CSRF token in the head and in each form will all be distinct.

Having different tokens in each form is a security enhancement added since Rails 5, which lower the risk of form hijacking.

@reddyonrails
Copy link

reddyonrails commented Jun 11, 2019

Yeah @kramer33 you are right about

$.rails.refreshCSRFTokens() on every Turbolinks CHANGE event will not resolve this issue.

But I have different issue.
When page renders with more than form I see different tokens. The form render with Turbolinks.visit() function , gives me InvalidAuthenticityToken error even though it is different token. I will keep checking this issue.

version is rails-5.2.0

Screen Shot 2019-06-10 at 10 12 19 PM

@kevinrobinson
Copy link

I ran into this on Rails 5.2.0 as well, it seems like #22275 is not working as intended when UJS is involved (specifically, the method $.rails.refreshCSRFTokens), as others have pointed out in this thread here and here.

@mastahyeti @rafaelfranca @rails/security, you all were involved in #22275, and I believe this PR is reporting a case where the change that intended to require per-form CSRF tokens is not working as intended. There's a repro case, so I'm wondering why this was just closed. If you think this is too niche and not worth fixing, perhaps you could share more about that here?

Thanks! 👍

@brendon
Copy link
Contributor

brendon commented Jul 2, 2020

I must say, this one caused me some confusion also. It's definitely caused by $.rails.refreshCSRFTokens.

Since rails-ujs is on by default in new Rails applications, doesn't that make the entire per_form_csrf_tokens code essentially useless?

@durrellchamorro
Copy link

@reddyonrails I had the same issue as you. I solved it by replacing my button_to which was using a delete method with a simple_form_for. A regular form_for or a form_with would probably work too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants