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

"Null"ing out the Origin of a BLOB #74

Closed
hfhchan opened this issue Mar 16, 2017 · 20 comments
Closed

"Null"ing out the Origin of a BLOB #74

hfhchan opened this issue Mar 16, 2017 · 20 comments

Comments

@hfhchan
Copy link

hfhchan commented Mar 16, 2017

Is it possible to "null" out the origin of a blob?

Recently, Check Point exposed a bug in WhatsApp Web and Telegram Web which were not validating the MIME types of documents properly and directly opening them as a blob, leading to an XSS attack (http://blog.checkpoint.com/2017/03/15/check-point-discloses-vulnerability-whatsapp-telegram/).

Besides checking the MIME types, using iframe sandbox would have mitigated this problem. Using iframe sandbox would be preferred over MIME type checking in some situations, e.g. allowing SVGs but disabling foreign content. However, if the user right-clicked on the iframe and chose "Open in New Tab", the blob content would have been executed directly under the web.whatsapp.com origin.

Therefore, is it possible to "null" out the origin for blobs (setting them to an opaque origin), such that they cannot access anything from the web.whatsapp.com origin, sandboxed or not? Then effectively, the blob URI act like a data URI.

@annevk
Copy link
Member

annevk commented Mar 16, 2017

If we do anything here I think we should put it on Blob objects directly. That way if we eventually manage to migrate away of creating URL references to them to just assigning them to other things, you wouldn't become vulnerable.

@mikewest maybe CSP should offer something here as well?

@mikewest
Copy link
Member

I see. sandbox isn't enough because you're worried about potentially opening the blob in a top-level context where you don't have control, and can't send headers.

I wonder if it would be reasonable to have a more general solution by allowing the developer to attach headers to the blob at creation time which would be delivered along with the blob when it's loaded. That would solve a different problem whereby folks can bypass CSP by creating a blob and navigating to it in a top-level context.

@annevk
Copy link
Member

annevk commented Mar 16, 2017

You pretty much need to do it after-the-fact, since blobs can come from many places. Although I suppose you could wrap such blobs by a more secure variant. I was thinking that CSP could maybe just sandbox for an entire origin, but not before we have origin-wide policies I suppose.

@hfhchan
Copy link
Author

hfhchan commented Mar 16, 2017

I think using origin-wide CSP here might be overkill. For a orgin-wide policy, that also means people need to set headers on the server side instead of via JavaScript (potentially out of reach), and rely on some sort of automagic propagation provided by the browser (which could lead to obscure bugs). It also means webdevs need to ensure all inline scripts in the same origin are hashed, which could be infeasible for large websites.

My thoughs are something like:

$blob = new Blob();
$blob->setOrigin(null);

where setOrigin will accept the current origin, a super-domain, or NULL for a opaque origin.

By allowing a blob to be set to an opaque origin, this opens up for use-cases where isolated JavaScript execution is desired. For example, people can send JavaScript-powered e-cards through WhatsApp without the e-card having access to web.whatsapp.com origin. Currently this isolation can only be done by using iframe sandbox or spawning new windows with data URIs.

Hopefully, browsers can just reuse the existing machinery for treating Data URIs.

@mikewest
Copy link
Member

This came up in conversation again today with some folks on Google's security team who are reviewing some of our products that stuff not-entirely-trustworthy data into blobs. We've come up with some workarounds (like creating the blob inside a sandbox srcdoc iframe), but I think we'd happily use a built-in mechanism if we can agree on something reasonable.

A specific solution to the narrow problem of moving a blob to an opaque origin could certainly be addressed as @hfhchan suggests, by adding an attribute to the blob object, and then adjusting the https://w3c.github.io/FileAPI/#unicodeBlobURL algorithm to serialize the origin as null if the attribute's value is set. (I'm not enthusiastic about allowing developers to pick an origin, document.domain style. Let's avoid that unless we have a very good reason not to.):

var b = new Blob( [ "<!-- some HTML goes here -->" ], { type: "text/html" } );
b.opaqueOriginOrSomeBetterName = true;
var u = URL.createObjectURL(b);
// u == "blob:null/uuid-uuid-uuid-uuid"

An alternative might add an opaque property to the BlobPropertyBag used when creating the blob:

var b = new Blob( [ "<!-- some HTML goes here -->" ], { type: "text/html", opaque: true } );
var u = URL.createObjectURL(b);
// u == "blob:null/uuid-uuid-uuid-uuid"

I guess we could also tack it onto createObjectURL (e.g. URL.createObjectURL(b, { opaque: true })), but if there are plans to migrate away from URL references to blobs (what's the alternative?), then this wouldn't be a good plan.

@mikewest
Copy link
Member

/cc @mkruisselbrink @inexorabletash

@annevk
Copy link
Member

annevk commented Jun 23, 2017

The alternative to string references to Blob objects is using object references, with appropriate added API support: <img>.objectSrc = blob.

It seems better to modify the constructor than modifying security properties post-creation, but that will require wrapping Blob objects returned from fetch() or some such.

@hfhchan
Copy link
Author

hfhchan commented Jun 26, 2017

opaque: true sounds like a good idea.

There might be valid use-cases to pass blobs up to the super domain, but I can't think of any at this moment.

@xtofian
Copy link

xtofian commented Jun 28, 2017

To add a bit of background regarding security concerns: With the specs as currently written, even just calling URL.createObjectURL(b) on a blob consisting of attacker-controlled content (i.e. making said blob URL-addressable) is potentially/theoretically risky: The Blob URL essentially consists of a UUID, and the spec refers to RFC 4122 for UUIDs, whose security section in turn explicitly states "Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access), for example." This means that as far as the specs are concerned, we have to assume that it's possible for a malicious page in a different origin, in the same browser, to guess the URL of a blob once createObjectURL has been called. It could then for instance load an iframe or instantiate a plugin from the blob's content; this would give the attacker script execution in the blob's (and hence its creating app's) origin.

This is probably not a particularly feasible attack in practice (there's likelyi enough entropy in GUIDs to make them too hard to guess in this scenario); but it seems unsatisfactory to rely on that.

**

@annevk -- <img>.objectSrc = blob would seem to address this concern too, since then there's never a URL that another page could guess and reference.

One use case for Blob URLs is to implement download/save of files whose content a browser app has as a JS value (e.g. received from an RPC, or cached in local storage). The app would create a Blob and a URL for it, and then instantiate a <a href="{{blobUrl}}" download> element.

Would objectSrc work with HTMLAnchorElement too (i.e. aEl.download = true; aEl.objectSrc = blob;) ? Presumably, right-click+open-in-new-tab should not work on such a link, since there's no URL/href.

@annevk
Copy link
Member

annevk commented Jun 28, 2017

It could then for instance load an iframe or instantiate a plugin from the blob's content; this would give the attacker script execution in the blob's (and hence its creating app's) origin.

How? Would the blob iframe and its parent not be cross-origin? Or you assume the blob iframe has unsafe postMessage() usage or some such?

@annevk
Copy link
Member

annevk commented Jun 28, 2017

(We could have <a>.objectHref or some such as well. We still haven't quite decided I think to add that feature and get it implemented all over.)

@jacksonloper
Copy link

Any updates on this or other sandboxing-blobs ideas?

@mkruisselbrink
Copy link
Collaborator

No real updated. Thinking about it more, I do wonder how the proposed behavior would interact with something like whatwg/fetch#666 though. At least today some/most browsers seem to block all cross origin navigations (and non-navigations) to blob URLs. So anything that would result in an opaque-origined blob URL would effectively result in a blob URL that can't be used for anything...

@annevk
Copy link
Member

annevk commented Oct 15, 2018

Yeah, I think we should first figure out the scope and lifetimes of blobs a little better.

@koto
Copy link
Member

koto commented Mar 16, 2020

Re: CSP integration, I think there is a way of nulling the Blob origins, making them useful for e.g. downloads, but not for direct DOM access when navigated to.

If https://fetch.spec.whatwg.org/#main-fetch step 5 did not overwrite the response tainting flag to "basic" for "navigate" requests, some directive's pre-navigation check could set the request's response tainting flag to "opaque". If I understand correctly, then navigations to all blob: URLs would be cross origin.

/ cc @mikewest @annevk - this would be addressing b) from #142 (comment)

@shhnjk
Copy link
Member

shhnjk commented Feb 17, 2021

I would love to see this moving forward. This is also one of the platform-level Trusted Types bypass vector. If we have the null origin option, we could throw a Trusted Types violation in URL.createObjectURL construction without the null origin option.

Updated: Since Blob object might come from response of fetch, throwing Trusted Types check in Blob object isn't a good fit.

@annevk
Copy link
Member

annevk commented Feb 17, 2021

@shhnjk sure, make progress on whatwg/fetch#666 and related issues (as well as policy containers and such) and this will be easier to define. But that work needs to happen first.

@annevk
Copy link
Member

annevk commented Feb 23, 2023

Let's close this as the discussion continued in #192.

@annevk annevk closed this as completed Feb 23, 2023
@KaYaDoR01

This comment was marked as spam.

@KaYaDoR01

This comment was marked as spam.

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

9 participants