---
# System prepended metadata

title: Proposal for enabling web apps full and first access and control over all same origin realms

---

# Proposal for enabling web apps full and first access and control over all same origin realms

## tl;dr

* Web applications as of today **have no control/visibility over newborn same origin realms in runtime**.
* When loaded, **same origin realms should be processed primarily by the app and only afterwards by their creator**, to allow the app to decide how same origin realms should behave as their host, just like how the app has the right to set its own CSP.
* This is not new as a concept, as **this exact ability is granted by the browser to extensions and plugins** in the form of the manifest field [`all_frames`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts#:~:text=Description-,all_frames,-Boolean), we just think **it makes sense to grant such power over child frames to the web app too**.
* We have a **working shim called [Snow ❄️](https://github.com/lavamoat/snow/)** of how we imagine a solution to this problem should behave, which is already integrated into [MetaMask 🦊](https://github.com/MetaMask/metamask-extension/) ([merged](https://github.com/MetaMask/metamask-extension/pull/15580)) and is used to enhance its protection.
* The main use case for this idea is for 1st/3rd party services who wish to **apply certain dynamic rules to a web app which are very incomplete when are only applied to the top main realm but not to all realms automatically**.
    * To further explore the different use cases jump over to [Motivation](#Motivation).

> *To further explore this idea, its motivation, the realms security problem and Snow - jump over to the [Resources](#Resources) section at the bottom of the page.*

## Introduction

We believe a **web app should have full and first control over newborn [realms](https://weizman.github.io/page-what-is-a-realm-in-js/)** within itself before any other entity does, including the creator of any certain realm.

In other words, if a certain script (whether of the app or of a 3rd party) lifts up a new realm for whatever reason, the [window proxy](https://developer.mozilla.org/en-US/docs/Glossary/WindowProxy) representing that realm should **first be processed by the app itself and only afterwards by that certain script**.

### Clarification

So for example, if web app `https://my-app.com` loads a [3rd party service](https://web.dev/optimizing-content-efficiency-loading-third-party-javascript/#what-do-we-mean-by-third-party-scripts) JS script from `https://external-service/sdk.js` and that service uses [iframes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) as part of its logic, the iframe naturally will load with its own realm.

This is very common for many reasons, a common example would be ad services such as [Google Ads](https://ads.google.com/) that optionally load ads within iframes.

In today's landscape, the context of web apps such as `https://my-app.com` has **no control over such rising realms and therefore cannot limit/define their power/abilities/behaviour**. 

#### To solve this problem, we: 
1. suggest a [proposal](#Proposal) for a new API; 
2. would like to present a [shim](#Shim) that implements fully this proposed API.

### Proposal

We imagine support for a new window owned property `"XXX"` that its descriptor is as follows:

```javascript
{ value: XXX, writable: true, enumerable: false, configurable: true }
```

Where `XXX` is a function that gets a callback which will be called with every new realm that comes to life within the app:

```javascript
XXX((win) => console.log('Here is a new window:', win));
```

Listing the callback by calling `XXX` should only be possible:
1. **Once** (per page load);
2. **By the web app itself** (meaning, by a script that originates in the same origin as the app, similar to how [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is also a privilege that only belongs to the web app);
3. **By the top most window** (aka [`top`](https://developer.mozilla.org/en-US/docs/Web/API/Window/top)).

#### Important notes

1. The callback should be called **synchronously after the creation of the realm**, so that the registrer of the callback assured to be getting first access to the realm before any other entity in the app.
2. **If the same realm was already introduced to the page, the callback will fire again anyway.** This is because there are cases in which the same realm representation - the `WindowProxy` object - is loaded more than once (e.g. iframe `src` relocation).
3. **The browser should pass to the registered callback same origin realms only**, as those are the true potential danger to the web app. Cross origin realms cannot synchronously access the web app and therefore cannot be leveraged maliciously against the app.

### Shim

I created [Snow ❄️](https://github.com/lavamoat/snow) which is a shim that when is applied to the web app at the beginning of its execution, **fully applies the above proposal to the execution runtime.**

Once applied, a new property `SNOW` is exported via the [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) which accepts the described callback and calls it with every new realm that comes to life within the app.

Being a shim, Snow achieves its goal by hooking into all the different points where new realms are formed in JavaScript and makes sure to pass all new realms to the callback before returning them to their creator.

However, being a shim also means Snow comes with clear disadvantages such as impacting performance, potentially not being bulletproof and more.

Therefore, **Implementing Snow as part of the browser will allow us to get the advantages it brings with it without these disadvantages.**

## Motivation

The motivation here is to simply **endow the web app with the capabilities that it deserves to have, which is governance and control over same origin realms within itself**. 

* This isn't a new concept - **this exact ability is granted by the browser to extensions and plugins** in the form of the manifest field [all_frames](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts#:~:text=Description-,all_frames,-Boolean), we just think it makes sense to grant such power over child frames to the web app too. 
    * With the minor exception in which extensions get primer access to all child frames whether cross or same origin whereas the web app should only have power over the realms under its own origin.


#### Here we'll explore the different reasons and use cases of why we might want to realize this proposal.

### 1. Third Party Vendors For Client Side Security Products

> Part A - The problem with not having builtin browser support:

As earlier described and demonstrated in the [Clarification](#Clarification) section, iframes are being used today in all sorts of legitimate ways.

However, in addition to the legitimate usage of them, **iframes can also easily be leveraged maliciously in web security context.**

This is **far more relevant today** than before due to the major shift web apps builders took towards fully relying on [3rd party dependencies](https://cleverbeagle.com/blog/articles/what-are-javascript-packages-and-dependencies) when building their apps.

That is because [securing a supply chain](https://www.techtarget.com/searcherp/definition/supply-chain-security#:~:text=Supply%20chain%20security%20is%20the,part%20of%20a%20supply%20chain.), which was less of a thing a couple of years back, is currently a very difficult problem to solve.

With the rise of supply chain attacks against web apps, different protection solutions (see "Part C" for examples) came to life, many of them around securing the app and browser builtin sensitive APIs in runtime.

The motivation was basically "It's hard to tell what part of the supply chain will be breached and how, so let's apply protection to the app itself in realtime, so that when an attacker finds a breach, we'll already be defending the app".

**It's hard to tell how an attacker will find its way into a supply chain, but might be easier to predict what they'll try to do once they're in.**


Here's an example: if the attacker attempts to steal the contents of [`document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) from the web page and exfiltrate it to its malicious domain, it might be smart to apply protection around access to the JavaScript API of `document.cookie`. Such protection can be applied by what is called "[monkey patching](https://dev.to/napoleon039/monkey-patching-what-is-it-and-should-you-be-using-it-50db)" - a technique that is used to override builtin APIs provided by the browser with custom hooks:

```javascript
/* defender */
const desc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
desc.get = () => ''; // disable document.cookie API getter
Object.defineProperty(Document.prototype, 'cookie', desc);
```

That is of course an oversimplification, but in that manner it is possible to apply robust protection around different sensitive APIs and by that encounter an attacker more prepared in the battlefield.

**However**, such protections are **basically worthless** due to the power that realms inherently bring with them, because the attacker can easily bypass the protection demonstrated above by simply leveraging another instance of the `document.cookie` API from within the realm of an iframe:

```javascript
/* attacker */
document.body.appendChild(document.createElement('iframe')).contentWindow.document.cookie;
```

While the example of protecting the top main realm's `document.cookie` API is simple, dynamically defending every new `document.cookie` API that might be brought to life within newborn realms is not possible as of today.

> Part B - How builtin browser support can help solve the problem:

If the proposal described above makes it through, we could use the new suggested API to **automatically apply our protection to all child realms with no effort**:

```javascript
/* defender */
top.XXX((win) => {
    const desc = win.Object.getOwnPropertyDescriptor(win.Document.prototype, 'cookie');
    desc.get = () => ''; // disable document.cookie API getter
    win.Object.defineProperty(win.Document.prototype, 'cookie', desc);
});
```

The registered callback will process every new same origin realm that comes to life **first** and will apply our `document.cookie` API protection to it, thus leaving no option to leverage new realms to retrieve the original behaviour of the protected API.

> Part C - Actors benefiting from this:

As mentioned in "Part A" above, there are a number of actors in the client side supply chain security industry that would highly benefit from being able to apply their security mechanizms automatically to all realms, some of them (but not all) are:

* [PerimeterX's Code Defender](https://www.humansecurity.com/products/code-defender)
* [Akamai's Page Integrity Manager](https://www.akamai.com/products/page-integrity-manager)
* [Imperva's Client Side Protection](https://www.imperva.com/products/client-side-protection/)

### 2. Client Side Monitoring Tools

> Part A - The problem with not having builtin browser support:

There are third party services that provide monitoring tools for web apps to have better insights into how their apps function and behave "in the wild".

To achieve that those usually apply monkey patches to different builtin APIs in the web app's runtime to have visibility into how, why and how many times are they being used.

For example, the popular among those - [Sentry.io](https://sentry.io/) - [patches different APIs such as `console`](https://github.com/getsentry/sentry-javascript/blob/c6f9dfe9570369c9acabec988f8a573d08bd8c82/packages/utils/src/instrument.ts#L108) to have real visibility into the `console` API usage of the web app in realtime.

Like before, this is a common practice among services like this, and they hook into many APIs than just `console`.

However, their visibility is incomplete, because in web apps where there is some to a lot of iframes usage, such services **do not extend their visibility into such iframes and therefore miss a big part of the picture.**

> Part B - How builtin browser support can help solve the problem:

If the proposal described above makes it through, we could use the new suggested API to automatically apply those visibility-purposed patches into all iframes within the page too, thus **significantly enhancing the quality of the service such third party vendors come to supply.**

> Part C - Actors benefiting from this:

As mentioned in "Part A" above, there are a number of actors in the client side visibility and monitoring industry that would highly benefit from being able to apply their tracking abilities automatically to all realms, some of them (but not all) are:

* [Sentry.io](https://sentry.io)
* [LogRocket.com](https://logrocket.com/)

### 3. Importance For Technological Innovation In JS

> Part A - The problem with not having builtin browser support:

There are innovative projects (different shims mostly) that would benefit from being able to apply themselves to all same origin realms automatically, partly because they're trying to apply technology to the page that needs to be deterministic and hermetic, which sometimes can be hard to achieve with multiple realms.

> Part B - How builtin browser support can help solve the problem:

The ability to automatically apply such logic to all realms can help enhance its purpose and keep it fully secure.

> Part C - Actors benefiting from this:

Some examples in that context:

* Part of the [SES shim](https://github.com/endojs/endo/tree/master/packages/ses) initiative is a strong capability called [lockdown](https://github.com/endojs/endo/tree/master/packages/ses#lockdown) which when is called freezes all platform intrinsics so that they could not be redefined and therefore trusted to function purely. However, calling lockdown only applies to the top main realm, and having the ability to apply it to all same origin realms automatically would have been beneficial for apps that wish to defend themselves using lockdown for their inner iframe activities too.
* [Across](https://github.com/lavamoat/across/) is a shim that introduces a way for two scripts within a single DOM to exchange messages with each other securely based on their origins. In order for this shim to fulfil its purpose safely, it must apply itself to all child realms, otherwise a pure realm can be leveraged to bypass its mechanizm. To achieve that, currently Across is built on top of Snow shim, but obviously replacing Snow with a builtin API would be far better.

### 4. More

We believe there are more examples for use cases, and there will be more in the future if this proposal goes through.

## TBDs

Although the idea and the need are quite clear to us, there are minor questions to still answer:

1. Should the API simply receive a callback? Or should the callback be registered to a new event perhaps?
2. Should the API receive only one callback once? Or should it allow multiple callbacks registration chronologically?
3. What should be the name of the official API/event? (Not Snow obviously)
4. Should the descriptor of the API be configurable/writable?
5. Should the callback be called only with same origin realms? or with cross origin realms too? (even though the callback code won't have sync access to newborn cross origin realms in such a case).

## Resources

This proposal derives from a long term research around the security aspect of realms, and hard work on bringing Snow to life mostly to learn that it's better off natively in the browser - here are some resources in this context:

* [awesome javascript realms security ⭐️](https://github.com/weizman/awesome-javascript-realms-security) - Long term realms security research documentation which includes references to:
    * [Snow ❄️](https://github.com/lavamoat/snow) - Official shim repo.
    * [Introduction to Snow](https://github.com/lavamoat/snow/wiki/Introducing-Snow) - further reading on the rise of supply chain attacks and how it all lead to creating Snow.
    * [Integrating Snow into MetaMask](https://weizman.github.io/page-snow-into-metamask/) - Going deeper on why Snow is an important component for enhancing the defense of MetaMask and any other web app basically.
    * [Live demo](https://lavamoat.github.io/snow/demo/) - a super simple demo to demonstrate Snow in action.