-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
CSRF protection from cross-origin <script> tags #13345
Conversation
end | ||
|
||
def non_xhr_javascript_response? | ||
content_type =~ %r(\Atext/javascript) && !request.xhr? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This content type comparison is gross but I couldn't find a better alternative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can content type be some other like application/javascript
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
text/ecmascript
and application/ecmascript
are also valid values and I think there are more supported content types for javascript execution.
Whitelisting is not an option right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unless I'm mistaken, doesn't the Mime types already take care of this?
request.format == :js && !request.xhr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pothibo request.format
is the requested format, not the response Content-Type.
For example, in #test_should_only_allow_same_origin_js_get_with_xhr_header
:
> [content_type, request.format, rendered_format]
=> ["text/javascript", #<Mime::Type:0x007ff4d96d15e8 @synonyms=["application/xhtml+xml"], @symbol=:html, @string="text/html">, #<Mime::NullType:0x007ff4d8438530>]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rafaelfranca You'll only get those Content-Type if you manually specify them with render ..., type: 'application/ecmascript'
. Mime::JS
will always be text/javascript
.
We may want a whitelist to extend verification to other Content-Type (including JSON for those supporting ancient browsers) in the future, but I don't think it's necessary to start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
👍 Thanks! |
@@ -89,6 +98,8 @@ def protect_from_forgery(options = {}) | |||
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session) | |||
self.request_forgery_protection_token ||= :authenticity_token | |||
prepend_before_action :verify_authenticity_token, options | |||
prepend_before_action :mark_for_same_origin_verification, options | |||
append_after_action :verify_same_origin_request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If people can skip verify_authenticity_token
in a controller that inherits protect_from_forgery
, then mark_for_same_origin_verification
would still be triggered and thus verify_same_origin_request
too, not? Would they need to skip that too, independently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I'll move the marker to verify_authenticity_token
to fix that.
I used a separate, private method because we encourage people to change the builtin method:
# The actual before_action that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
So an overriding method would miss the marking and skip same-origin verification.
I'm removing that comment. Shouldn't be overriding that method.
Tests are broken 😢 |
@@ -1,3 +1,8 @@ | |||
* Extend protect_from_forgery to GET requests with JavaScript responses, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extend protect_from_forgery
to GET requests with JavaScript responses,
Thanks to @homakov for sounding the alarm about JSONP-style data leaking
@rafaelfranca fixed the other broken tests, they hit the same-origin verification 😁 |
👍 |
CSRF protection from cross-origin <script> tags
@jeremy probably instead of exception it should respond with "please send x requested with header to verify request origin" |
@homakov thought about that.. responding with 403 Forbidden so it's clear it's a client error not an application error App devs can use In any case, this is starting as small as possible. I hope we'll see PRs that do a default |
@TechFounder Sounds like you're making a GET request that has a JavaScript response, but you aren't passing the |
In our production server i'm getting Am i seeing these inconsistencies because of browser differences? I'm using |
@harmdewit did you ever figure this out? I'm having the same issue. |
I'm not sure what the issue was after this long time but putting this into our ApplicationController seemed to fix it + def non_xhr_javascript_response?
+ unless request.get?
+ content_type =~ %r(\Atext/javascript) && !request.xhr?
+ end
+ end |
Thanks @harmdewit! 😃 |
I'm not sure what is requesting format=/ either. Another solution I came up with is to reorder the So I changed this: respond_to do |format|
format.js do
...
end
format.html
end to this: respond_to do |format|
format.html
format.js do
...
end
end That way format=/ will return html. |
rails/rails#13345 introduced CSRF protection to gets with JavaScript responses.
rails/rails#13345 introduced CSRF protection to gets with JavaScript responses.
Extend
protect_from_forgery
to cover GET requests with JavaScript responses, protecting apps from cross-origin<script>
embedding that may leak sensitive data.TODO
Accept
rather than an explicit formatAccept