-
Notifications
You must be signed in to change notification settings - Fork 509
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
Missing X-CSRF-Token Header on AJAX POST Request with File Upload #451
Comments
It is the expected behavior. If the form has file inputs it will fallback to standard POST, so it will not need to send the header. |
But how does the csrf token from the meta tags get included then? And why am I getting an invalid authenticity token error then? |
The form usually include the authenticity token. Check if it is being included in the form. |
You mean as a hidden input? No. It isn't. |
Yes, the hidden input. So that is why it is not working. |
Iiinteresting.. thanks a ton! |
@rafaelfranca so doing some more investigation into this, I seem to have figured out the source of the problem here. I'm not sure if you'd like to reopen this issue or if I should make a new one (please advise), but this definitely seems to warrant further discussion/analysis.. Basically, this problem seems to be a case of both the action_view gem and the jquery-ujs gems each thinking that the other is handling the inclusion of the authenticity token when there is a file field in a form is submitted via AJAX. (See https://github.com/rails/rails/blob/621ed494f573c4e37c1f7d37cc8741cc4c502827/actionview/lib/action_view/helpers/form_tag_helper.rb#L20 on lines 20, 31 and 38 for further details.) According to the source code comments there, a simple workaround would be to set Might I propose solving this conflict by catching submit events for AJAX POST forms with file uploads and creating/updating a hidden authenticity_token input field (from the token in the meta tags) before letting the submission through? If that route is not chosen, I really think there should at least be better warnings/documentation for this situation, since, AFAIK, there is currently none whatsoever, neither in Rails nor in this gem. (By "warnings", I mean that perhaps an error should be thrown in ActionView if a Please let me know if I should also be creating a concurrent issue in the Rails repository. |
I believe we should take in consideration rendering the hidden field if |
I can certainly try my hand at it.. Should have one within a week (if not much sooner). |
Should this issue be reopened in the interim in case others have ideas/input? |
@rafaelfranca Wait, to clarify, when you say "rendering the hidden field", are you suggesting doing this at submit-time via JS in jquery-ujs (as I suggested) or inside ActionView? |
Inside action view. It is the best place to know about this case. |
@rafaelfranca I have created issue rails/rails#22807 in the rails repo to ensure that whatever PR I write, the repo owner is prepared to merge it. Please comment there and share your perspective. |
rails/rails#22807 doesn't have any replies yet, unfortunately, so I'm tempted to just submit a PR to this repo to implement the fix the way I mentioned above. I know you said you think the fix belongs in |
WorkaroundFYI, this doesn't resolve this issue, but one workaround I've discovered is to include a javascript plugin that enables support for POST XHR requests with file inputs. For example, if the following javascripts are
And then something the following code is run at page load:
All POST requests marked as "remote" will be submitted via XHR requests, even if they have file inputs. |
@rafaelfranca Maybe this behaviour should be revisited as the latest browsers support HTML5 APIs to perform file uploads via AJAX, with XHR2 it even supports upload progress without any server-side changes. For non-supported browsers it can fallback to a POST as it does currently. |
Wow, I just ran into this, and it was painful to track down. To me, it seems like jquery-ujs is responsible for all the JavaScript logic of remote: true, so before submitting the form, if the form has a file input, it should fetch the CSRF value from the <%= csrf_meta_tags %>, dynamically create a hidden element for the CSRF token on the form on-the-fly, and then submit the form. This would necessatate changes to documentation to indicate that the security improvements of per-form CSRF tokens do not apply to forms with file inputs:
It seems like this approach:
|
Another workaroundAn easy workaround is to add <%= form_for @user, remote: true, authenticity_token: true do |f| %>
<%= f.file_field :image %>
<% end %> That workaround is easy if you're not fragment caching forms, and ignores the documentation's statement that you should only use the
Perhaps another way to "fix" this is to always include the authenticity token, and to instruct users who care about fragment caching to use |
I like the solution proposed here #451 (comment). Could you work on it? |
Considering this solution is (as @aguynamedben noted) identical to my original suggestion, it will hopefully come as a shock to no one that I concur with it. 😄 @aguynamedben,If you'd be willing to do this, that would be great. (Free time is hard to come by these days.) I was hoping to do this way back when I submitted it, but I was confused by @rafaelfranca's suggestion that the change be made within It's worth pointing out that I raised this issue before Rails 5 was released, and thus before per-form CSRF tokens were a thing. Given their advent and the security benefits they confer, I think it's worth factoring in a version of your another workaround into this patch, which would allow people who don't use fragment caching to reap the benefits of per-form CSRF tokens. I would make a tweak though: I'd include per-form CSRF tokens by default (for remote forms too) and automagically detect if the @lucaspiller,I agree that your suggestion should be implemented, but IMHO, that exceeds the scope of this issue. Having |
I'm trying to trace either some sort of conflict on my end (my app is rather complex, albeit not really JS-wise) or a bug in jquery-ujs which I have tentatively traced to the
nonBlankFileInputs
variable. (That is the bug seems to occur, if thisif
block is true: https://github.com/rails/jquery-ujs/blob/master/src/rails.js#L477)Basically, I have a form that sends correct AJAX requests (correct meaning including the X-CSRF-Token header) as long as, and only as long as, its file input field left empty. As soon as a file input field is selected the header is no longer sent (and I think the request is also no longer handled via AJAX).
Some quirky things about this form that may be contributing to this bug are
simple_form
gem instead of the defaultform_for
tags.display: none
submit button within the<form>
tags and then triggering it with javascript from the bootbox code with$('div.bootbox div.bootbox-body').find('input[type=submit]').first().click();
. This has worked perfectly so far though.Any advice on how I can trace this problem further would be greatly appreciated.
The text was updated successfully, but these errors were encountered: