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

Force :attachment as content disposition for some content types #31639

Merged
merged 1 commit into from Jan 5, 2018

Conversation

Projects
None yet
3 participants
@rosa
Member

rosa commented Jan 5, 2018

Summary

Currently, unless a value for disposition is passed to ActiveStorage::Blob#service_url we use inline for everything. However, some content types shouldn't be rendered inline by default. For example:

  • Sending .ai files as application/postscript to Safari opens them in a blank, grey screen, and downloading .ai as application/postscript files in Safari appends .ps to the extension.
  • Sending HTML, SVG, XML and SWF files as binary closes XSS vulnerabilities. Even if these are mitigated by serving the blobs from a different domain from your application, having an HTML file rendered by the browser instead of downloaded opens the door for more realistic phishing attacks.
  • Sending JS files as binary avoids InvalidCrossOriginRequest without compromising security.

This pull request modifies this behaviour to force :attachment as disposition for specific content types like the above, configured in config.active_storage.content_types_to_serve_as_binary.

Other Information

I have included a default list of content types for this new config option, but it could be left empty if you think that makes more sense, and let users decide what they want to be treated as attachments. I think the default list makes sense, though.

r? @georgeclaghorn

@rails-bot

This comment has been minimized.

Show comment
Hide comment
@rails-bot

rails-bot Jan 5, 2018

Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @georgeclaghorn (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

This repository is being automatically checked for code quality issues using Code Climate. You can see results for this analysis in the PR status below. Newly introduced issues should be fixed before a Pull Request is considered ready to review.

Please see the contribution instructions for more information.

rails-bot commented Jan 5, 2018

Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @georgeclaghorn (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

This repository is being automatically checked for code quality issues using Code Climate. You can see results for this analysis in the PR status below. Newly introduced issues should be fixed before a Pull Request is considered ready to review.

Please see the contribution instructions for more information.

Show outdated Hide outdated activestorage/lib/active_storage/engine.rb Outdated
Show outdated Hide outdated activestorage/app/models/active_storage/blob.rb Outdated
@georgeclaghorn

Nice work, Rosa!

Show outdated Hide outdated activestorage/app/models/active_storage/blob.rb Outdated
Show outdated Hide outdated activestorage/lib/active_storage/engine.rb Outdated
Show outdated Hide outdated activestorage/lib/active_storage/engine.rb Outdated
@@ -201,8 +201,8 @@ def representable?
# with users. Instead, the +service_url+ should only be exposed as a redirect from a stable, possibly authenticated URL.
# Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And
# it allows permanent URLs that redirect to the +service_url+ to be cached in the view.
def service_url(expires_in: service.url_expires_in, disposition: "inline")
service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type
def service_url(expires_in: service.url_expires_in, disposition: :inline)

This comment has been minimized.

@rosa

rosa Jan 5, 2018

Member

I've changed this from "inline" to :inline because I've seen it used as symbol in other places like:

def service_url(expires_in: service.url_expires_in, disposition: :inline)
service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type
end

# Returns a signed, temporary URL for the file at the +key+. The URL will be valid for the amount
# of seconds specified in +expires_in+. You most also provide the +disposition+ (+:inline+ or +:attachment+),
# +filename+, and +content_type+ that you wish the file to be served with on request.

@rosa

rosa Jan 5, 2018

Member

I've changed this from "inline" to :inline because I've seen it used as symbol in other places like:

def service_url(expires_in: service.url_expires_in, disposition: :inline)
service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type
end

# Returns a signed, temporary URL for the file at the +key+. The URL will be valid for the amount
# of seconds specified in +expires_in+. You most also provide the +disposition+ (+:inline+ or +:attachment+),
# +filename+, and +content_type+ that you wish the file to be served with on request.

@rosa

This comment has been minimized.

Show comment
Hide comment
@rosa

rosa Jan 5, 2018

Member

@georgeclaghorn, thanks a lot for your review! I have made the changes you suggested, and also, as you very correctly pointed out, I changed the code to force the content disposition to attachment instead of defaulting to it. Otherwise this wouldn't solve the security concerns, as an attacker could simply add disposition=inline to the URL to get the same effect as now. I have also updated the pull request description to match this new behaviour.

I was thinking... should this be included in the CHANGELOG?

Member

rosa commented Jan 5, 2018

@georgeclaghorn, thanks a lot for your review! I have made the changes you suggested, and also, as you very correctly pointed out, I changed the code to force the content disposition to attachment instead of defaulting to it. Otherwise this wouldn't solve the security concerns, as an attacker could simply add disposition=inline to the URL to get the same effect as now. I have also updated the pull request description to match this new behaviour.

I was thinking... should this be included in the CHANGELOG?

@rosa rosa changed the title from Use :attachment as default content disposition for some content types to Force :attachment as content disposition for some content types Jan 5, 2018

Show outdated Hide outdated activestorage/app/models/active_storage/blob.rb Outdated
@georgeclaghorn

This comment has been minimized.

Show comment
Hide comment
@georgeclaghorn

georgeclaghorn Jan 5, 2018

Member

I was thinking... should this be included in the CHANGELOG?

Yes please!

Member

georgeclaghorn commented Jan 5, 2018

I was thinking... should this be included in the CHANGELOG?

Yes please!

@georgeclaghorn

❤️

Can you squash your commits into one?

@rosa

This comment has been minimized.

Show comment
Hide comment
@rosa

rosa Jan 5, 2018

Member

Of course!

Member

rosa commented Jan 5, 2018

Of course!

Force content disposition to attachment for specific content types
In this way we avoid HTML, XML, SVG and other files that can be rendered
by the browser to be served inline by default. Depending on the origin
from where these files are served, this might lead to XSS
vulnerabilities, and in the best case, to more realistic phishing
attacks and open redirects.

We force it rather than falling back to it when other disposition is not
provided. Otherwise it would be possible for someone to force inline
just by passing `disposition=inline` in the URL.

The list of content types to be served as attachments is configurable.

@georgeclaghorn georgeclaghorn merged commit 1e51a38 into rails:master Jan 5, 2018

1 of 2 checks passed

continuous-integration/travis-ci/pr The Travis CI build could not complete due to an error
Details
codeclimate All good!
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment