A Web Platform API proposal for Blob URL
Jun Kokatsu, Feb 2023 (©2023, Google)
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.
- It has a format of
blob:scheme://[UUID]/UUID
. - It has a unique non-opaque origin (e.g.
https://[b9b68b26-a98b-4ad6-b089-33d2afa96944]
). - It does NOT inherit CSP from the creator.
- It is treated as cross-site to other URLs (except itself) when rendered as a document (e.g. in Site Isolation).
It aims to solve 2 problems.
Blob URL is useful for loading locally available resources. However it also leads to XSS bugs.
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).
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.
Good question! There has been discussion around adding a way to create opaque Blob URLs.
However, there are few issues in opaque Blob URLs.
- Access to cetain APIs are prohibited (e.g. cookie, localStorage, etc). Therefore code that touches any prohibited API causes breakage. Which isn't ideal for this proposal, because one of the goals is to provide a native alternative to sandbox domains.
- Opaque origins are serialized to "null". This makes it difficult for a Blob URL creator to send postMessages to the Blob URL iframe and ensure that it is received to the intended page (because any other opaque origin will have "null" origin).
Therefore, I think cross-origin Blob URLs are better!
While I'm okay with changing it to blob:scheme://UUID/UUID
or similar other format, here are some reasons.
- It is a clever idea.
- All requests that require an origin header will have
Origin: https://[UUID]
for cross-origin Blob URLs. This form of origin is very similar to IPv6 URLs (e.g. http://[2607:f8b0:400a:807::200e]). But as far as I know, thehttps://UUID
origin format does not exist today. Therefore, I thought it will cause less breakages.
A context (e.g. Window, Worker, etc) which called URL.createObjectURL
method to create a Blob URL is the creator (i.e. not the context which created a Blob object).
Yes!!!
We'd like to expose URL.getCreatorOrigin
method to the URL interface.
// script on blob:https://[UUID]/UUID
window.addEventListener('message', event => {
if (event.origin === URL.getCreatorOrigin(location.href)) {
console.log('message from a trusted creator!');
}
});
We could avoid exposing this method by including the creator origin in a cross-origin Blob URL (e.g. blob:https://[UUID]/example.com
), but I have a slight concern of phishing if the Blob URL is rendered on a top-level page.
Yes! You can use iframe sandbox and/or CSP Embedded Enforcement to apply CSP and/or sandbox.
<iframe sandbox="allow-scripts" csp="default-src 'self';" src="blob:https://[958c8e12-9f61-43a0-950a-56ecb19d3028]/958c8e12-9f61-43a0-950a-56ecb19d3028"></iframe>
You can also add a <meta>
tag with CSP in the Blob URL content to enforce CSP (but not sandbox).
A creator of cross-origin Blob URLs is often a sensitive origin. And therefore it might have strict CSP. However, the creator might want arbitrary scripts to run inside a cross-origin Blob URL (because it act like a sandbox domain), which is not possible if CSP is automatically inherited.
No, because cross-origin Blob URLs are treated as a cross-origin URL, it's similar to embedding any other cross-origin pages (which have their own CSP settings).
Yes! For sites with restrictive CSP such as frame-src 'self' blob:;
, cross-origin Blob URL iframes are blocked by default. And those sites would need to set 'allow-unique-blob'
keyword in frame-src
to opt into framing cross-origin Blob URLs.
Only pages which are same-origin with the cross-origin Blob URL creator can fetch or navigate to the cross-origin Blob URL.
Following folks provided insightful feedback which led to the shape of this proposal.
Damien Engels, David Dworken, terjanq, and Mike West.