A Web Platform API proposal to improve sandboxing and Blob URL
David Dworken and Jun Kokatsu, Aug 2023 (©2023, Google)
Add a new sandbox keyword, allow-unique-origin
, that causes the rendered content to execute in a unique non-null
origin.
The origin of a document sandboxed in this way will be sandbox:["$RANDOM_UUID","$PRECURSOR_ORIGIN"]
. For example, if https://example.com
set a CSP: sandbox allow-unique-origin
header, then the origin of the document would be sandbox:["9138ee47-c4f7-4e30-8751-acf51834e3f6","https://example.com"]
. Having a unique origin that contains the precursor origin, makes it possible to implement a number of useful product features like checking the precursor origin when responding to CORS requests.
These sandboxed pages will have access to isolated new storage partitions with a lifetime scoped to the current page. This includes document.cookie
, window.localStorage
, window.caches
, and more.
Since unique-origin documents are considered cross-origin (and cross-site) from the precursor origin (and any other origins), it should be process-isolated whenever possible by User Agents.
Expose a headers
option to BlobPropertyBag in the Blob constructor. The headers
option will take in an object containing response headers to associate with the Blob. Currently, supported headers are Content-Security-Policy
and Content-Disposition
.
This will enable a number of new uses for Blobs including creating Blob URLs that are guaranteed to not lead to XSS vulnerabilities:
const blob = new Blob([untrustedHTML], {
type: 'text/html',
headers: {
'Content-Security-Policy': 'sandbox allow-scripts allow-unique-origin'
},
});
const safeBlobUrl = URL.createObjectURL(blob);
iframe.src = safeBlobUrl;
window.open(safeBlobUrl);
In this case, safeBlobUrl
is guaranteed to render in a unique origin no matter how it is used, and thus it cannot lead to an XSS vulnerability in the creating application.
When a Blob sets a CSP policy that includes the allow-unique-origin
sandbox keyword, it does not inherit the CSP of the creator. This matches the web's model where loading an iframe with a distinct origin also does not inherit the CSP of the parent page.
It aims to solve three problems.
Blob URL is useful for loading locally available resources. However it also leads to XSS bugs when Blob URLs are created with untrusted content.
This proposal makes it possible to create sandboxed Blob URLs that are guaranteed not to cause an XSS (because any JS 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 bombing, and Spectre attacks), many sites rely on sandbox domains to get unique origins. Sandbox domains entail significant complexity for authentication (especially if one also adds it to the public suffix list) and are very hard to do right. Adding the ability to create Blob URLs, iframes, and top level pages with unique origins can replace this complexity with a native Web platform API.
CSP sandbox is widely used for isolating content, but running in a null
origin significantly limits many uses:
- Sandboxed content can't access any storage APIs. Many third-party scripts (e.g. analytics scripts) rely on being able to access these APIs, so it becomes impossible to sandbox these pages.
- Sandboxed content sends requests with a
Origin: null
header, so there is no way for services to make fine grained decisions based on exactly what content is executing in a given sandboxed page.
Supporting a unique origin, fixes both of these limitations and will enable more services to use sandboxing to isolate untrusted content.
Why not Safe Blob URL proposal?
Compared to that proposal, this doc proposes a more generic solution for creating unique origins that can be used for more than just Blob URLs. This makes it easier to use this feature to refactor existing web applications.
However, there is a downside to this proposal:
- It does not fully address XSS issues stemming from Blob URLs. SVG use elements could point to a same-origin sandboxed Blob URL and thus lead to unsandboxed code execution.
- This seems acceptable since this is a very rarely used feature.
Having origins like sandbox:["$RANDOM_UUID","$PRECURSOR_ORIGIN"]
make it possible for endpoints receiving CORS requests to ascertain both the original origin, and which unique origin a request is coming from. I'm not attached to this format, but I believe including this information is useful.
A creator of unique origin Blob URLs is often a sensitive origin. And therefore it is likely to have a strict CSP to defend against XSS attacks. However, the creator likely is using a sandboxed Blob URL in order to safely run arbitrary HTML/JS (because it acts like a sandbox domain), which is not possible if CSP is automatically inherited.
Conceptually, this isn't a CSP bypass since it is treating sandboxed Blob URLs identically to cross-origin iframes.
However, some websites use CSP for containment purposes. In order to ensure that this doesn't bypass those CSPs we'll add a 'allow-unique-blob'
CSP keyword. This means that a CSP like frame-src blob:
won't allow loading sandboxed blobs unless it is changed to frame-src blob: 'allow-unique-blob'
.
Thank you to the following folks who provided insightful feedback which shaped this proposal.
Damien Engels, terjanq, Artur Janc, and Mike West.