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

Proposal: Add a capability to create a Blob URL with a unique (non-opaque) origin #192

Closed
shhnjk opened this issue Feb 10, 2023 · 19 comments

Comments

@shhnjk
Copy link
Member

shhnjk commented Feb 10, 2023

Up-to-date details and FAQ for this proposal is available at https://github.com/shhnjk/Safe-Blob-URL

Proposal

Expose a crossOrigin option to BlobPropertyBag in the Blob constructor and a read-only crossOrigin property in Blob instances.

// script on https://example.com
const untrustedHTML = '<script>alert(document.domain)</script>';
const blob = new Blob([untrustedHTML], {
                 type: 'text/html',
                 crossOrigin: true
             });
if ('crossOrigin' in Blob.prototype && blob.crossOrigin) {
  const url = URL.createObjectURL(blob);
  console.log(url); // blob:https://[958c8e12-9f61-43a0-950a-56ecb19d3028]/958c8e12-9f61-43a0-950a-56ecb19d3028
}

A cross-origin Blob URL is special in a few ways.

  1. It has a format of blob:scheme://[UUID]/UUID.
  2. It has a unique non-opaque origin (e.g. https://[b9b68b26-a98b-4ad6-b089-33d2afa96944]).
  3. It does NOT inherit CSP from the creator.
  4. It is treated as cross-site to other URLs (except itself) when rendered as a document (e.g. in Site Isolation).

Why do we need this?

It aims to solve 2 problems.

XSS through Blob URLs

Blob URL is useful for loading locally available resources. However it also leads to XSS bugs.

  1. XSS on WhatsApp Web.
  2. XSS on Shopify.
  3. XSS on chat.mozilla.org.

The cross-origin Blob URL is designed in a way that these XSS won't happen (because a script will execute in a unique origin).

A native alternative to sandbox domains

Many Web apps require a place to host user contents (e.g. usercontent.goog, dropboxusercontent.com, etc) to safely render them. In order to do so securely (e.g. to avoid exploitable XSS, cookie bomb, and Spectre attacks), a site needs to register a sandbox domain, add it to the public suffix list, and then host user contents in randomly generated subdomains. However this is not something that any site can afford due to engineering and maintenance cost.

The cross-origin Blob URL provides a way to render user contents in a cross-site context without such setup.

@shhnjk
Copy link
Member Author

shhnjk commented Feb 10, 2023

CC: @annevk

@annevk
Copy link
Member

annevk commented Feb 13, 2023

I think crossOrigin is confusing given the feature in HTML with the same name that kinda means the opposite of this. Cross-origin blob: URL also seems confusing and doesn't really work well if we ever allow navigating to or downloading Blob objects directly.

This is probably the correct design, but it does make me a bit uneasy that we copy the policy container when doing URL.createObjectURL(). Now if origin becomes part of the policy container how would we reconcile this?

cc @antosart

@antosart
Copy link
Member

I just skimmed trough this, but this mentions that the blob would NOT inherit CSP from the creator, hence I believe it would not inherit any policies, hence we would not copy the policy container in this case...?

@annevk
Copy link
Member

annevk commented Feb 13, 2023

You're right, but that makes me wonder if this could be used to circumvent sandboxing. While an opaque origin has some unique restrictions, policies can go further.

@antosart
Copy link
Member

Yes. I think this makes things possible that were not possible before. For example, a page with default-src blob: should not be able to exfiltrate any information at the moment. After this proposal, it will be able via a cross-origin blob.

I am reading https://github.com/shhnjk/Safe-Blob-URL#is-there-a-way-to-block-cross-origin-blob-urls-in-iframe and I think it would be better if blob: in CSP does not allow cross-origin blobs, and instead a new keyword is needed for allowing framing them.

@shhnjk
Copy link
Member Author

shhnjk commented Feb 13, 2023

I think crossOrigin is confusing given the feature in HTML with the same name that kinda means the opposite of this.

How about changing the name to crossSite?

Cross-origin blob: URL also seems confusing and doesn't really work well if we ever allow navigating to or downloading Blob objects directly.

What does navigation to Blob objects look like?
Can this be solved if we expose this option to URL.createObjectURL() instead of Blob objects?
I.e.: URL.createObjectURL(blob, {crossOrigin: true})

Yes. I think this makes things possible that were not possible before. For example, a page with default-src blob: should not be able to exfiltrate any information at the moment. After this proposal, it will be able via a cross-origin blob.

I think you can already exfiltrate information today (PoC), because there is no restriction of navigations.

To me, opt-out (i.e. use keyword if you want to block unique Blob URL) makes sense because:

  1. To create a Blob, you need a script execution first. If that's an unintended script execution, then that's game over.
  2. The content of Blob can be untrustworthy, but in this case the person creating the Blob URL can apply CSP and/or sandbox with attributes or meta tag to restrict them.

I think that sites which are using tight CSP like default-src blob: are pretty rare, and if they want, they should have a way to restrict them. But that should not warrant inavailability for this API for others by default (which are the majority).

@annevk
Copy link
Member

annevk commented Feb 13, 2023

I'm not sure how using "site" is better? It has a defined meaning in HTML and it's not related to opaque origins.

What does navigation to Blob objects look like?

Imagine the navigation API gaining support for Blob objects.

I don't think it would be better if we did this through URL.createObjectURL() as that wouldn't help migration away from that API.

Exfiltration through top-level navigation is "fine" as that's "visible".

@shhnjk
Copy link
Member Author

shhnjk commented Feb 13, 2023

I'm not sure how using "site" is better? It has a defined meaning in HTML and it's not related to opaque origins.

Note that this proposal creates new "site", which is not an opaque origin. And because it is designed to always be cross-site when rendered as a document, the site definition in HTML clearly matches this API.

What does navigation to Blob objects look like?

Imagine the navigation API gaining support for Blob objects.

I think that's not an issue. Navigation API only supports same-origin endpoints for most of API (like navigate event). There are probably few places where cross-origin Blob "could be" supported (e.g. navigation.navigate), and we can decide to either block it, or convert the given blob into URL.

From this API point of view, we just need 2 things.

  1. When a unique Blob or Blob URL is rendered as a document, it is treated cross-site to the creator.
  2. When a unique Blob or Blob URL is used as a resource, it is origin tainted.

If we can acheve this with Blob objects (which I believe we can), then it's probably non-issue for navigating/downloading Blob objects.

Exfiltration through top-level navigation is "fine" as that's "visible".

I think we are in agreement that it's not a perfect protection then 🙂 And an attacker can call history.back() to hide it, so I doubt how "visible" it would be.

@eligrey
Copy link

eligrey commented Feb 15, 2023

If you're looking to block exfiltration via navigation, there's already the Navigation API for that. You could use an allowlist of navigation hosts or simply block all blob URLs.

@ddworken
Copy link

Hi all! I work with @shhnjk at Google, and have been thinking about an alternate API shape that is more broadly applicable while still solving many of the same goals. At a high level, my proposal is to:

  1. Support a new allow-unique-origin sandbox flag that can be used with both CSP sandbox and iframe#sandbox
  2. Add support for attaching response headers to created Blobs

You can see the full details in Sandbox allow-unique-origin and customizing Blob response headers. I'm curious to hear folks thoughts on this alternative shape which enables creation of unique origins for iframes and HTML responses.

cc: @mikewest @arturjanc

@annevk
Copy link
Member

annevk commented Feb 23, 2023

That still doesn't address the authority conflict: #192 (comment). I don't think we can ignore that.

Other feedback:

  1. This seems like it should be its own proposal to give it proper visibility, given how much complexity it adds to the web platform security story. "Unique origin" is also easily confused with "opaque origin".
  2. Blob with headers seems interesting. It would also allow for some download use cases people had as well. Processing model needs a lot more detail though.

@shhnjk
Copy link
Member Author

shhnjk commented Feb 23, 2023

That still doesn't address the authority conflict: #192 (comment). I don't think we can ignore that.

What do you consider "circumvent sandboxing"?
If that means CSP restrictions, is opt-in like frame-src blob: 'allow-unique-blob' enough to satisfy your concern?

@annevk
Copy link
Member

annevk commented Feb 24, 2023

I haven't done a thorough analysis as to whether that is the sole issue, but if it is, I think that would address my concern, yes.

@shhnjk
Copy link
Member Author

shhnjk commented Feb 24, 2023

Got it. Given @annevk and @antosart both believe that opt-in is necessary, I've added an opt-in requirement for sites which enforces frame-src (and default-src).

I guess there were other naming concern about crossOrigin. Maybe crossSite: true or unique: true might be better?

@annevk
Copy link
Member

annevk commented Mar 7, 2023

Could you and @ddworken first work out what proposal you actually want to pursue?

@shhnjk
Copy link
Member Author

shhnjk commented Mar 16, 2023

Within Google, we are inclining towards allow-unique-origin option (especially because it also solves an additional problem). But we'd love to hear opinions from Mozilla folks as well. CC: @mozfreddyb, @dveditz.

@shhnjk shhnjk changed the title Proposal: Add a crossOrigin option to Blob Proposal: Add a capability to create a Blob URL with a unique (non-opaque) origin Apr 17, 2023
@shhnjk
Copy link
Member Author

shhnjk commented May 31, 2023

From talking to a few Mozilla folks, they have good impression on the problems these proposals are aiming to solve, but it's too early to call for a position. Part of the reason is that there are 2 proposals which aren't specced (i.e. needs more details).

I think we should tackle allow-unique-origin option (reasoning), and brush out details (e.g. either to have a list of allowed headers).

@annevk, WDYT?

@annevk
Copy link
Member

annevk commented Jun 12, 2023

I don't think I have anything to add beyond my earlier comments at this stage. One of which was that if this goes beyond blob: URLs this should probably go elsewhere. Probably whatwg/html.

@shhnjk
Copy link
Member Author

shhnjk commented Aug 23, 2023

I don't think I have anything to add beyond my earlier comments at this stage. One of which was that if this goes beyond blob: URLs this should probably go elsewhere. Probably whatwg/html.

Thanks! I've opened whatwg/html#9623, to track this in whatwg/html. One we settle on discussion there, I will open an issue in File API to propose adding headers to Blob object (or I can do it now if that's prefered).

@shhnjk shhnjk closed this as completed Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants