MetaMask JavaScript Security Stack (Part 1 - scuttling) [𝕏]

Originally posted on X

MetaMask 🦊 is an amazing app for many reasons.

One reason I like especially is that even though it works just fine, the window object of the app is (almost) unusable!

If you’re into Browser JavaScript security, come learn about what we call β€œscuttling” - by LavaMoat πŸŒ‹


.. naturally, MetaMask 🦊 runs on top of a complex graph of dependencies.

Therefore, we're quite scared about supply chain attacks (you should too btw).

So scared that we build and run the app under a homemade tool we call LavaMoat πŸŒ‹.

By running on top of SES by @agoric,
LavaMoat creates a dedicated sandbox for each dependency.

Furthermore, it makes sure each sandboxed dep gets access only to the features it actually needs.

So for example, if dep "axios" needs access to "window.fetch", LavaMoat will make sure it gets that access.

But if..
.. "axios" gets an update where it tries to access "document.cookie" all of the sudden, its dedicated LavaMoat sandbox won't allow it, thus preventing the dep from accessing features it isn't supposed to have access to.

Pretty neat right?


What if a dep manages to use the APIs allowed by LavaMoat to escape the sandbox?

In that case, the dep can climb up to the real global object of the app (the window) where it can find all the APIs LavaMoat tried to deny it from!

Effectively, (almost) canceling LavaMoat's goal!
Luckily, we like to prepare for such edge cases in MetaMask βš”οΈ

The beauty about the β€œsandboxes” LavaMoat uses (called Compartments - implemented in SES by @agoric) is that the instances of the APIs they provide are not the same instances originally coming from the global object.
So if the entire dep-graph and the app itself all live in their own separate sandboxes with their own sets of APIs - this means the original APIs that came from the original global object are unused.

If so, why not remove them then?

In fact, it'll even make the app more secure!
That way, code escaping the sandbox won't be able to grab APIs it isn't allowed to!

So if a dep we don't trust is allowed to access "fetch", and somehow managed to escape its Compartment (/sandbox) and climb up to the window object, it'll expose much more APIs than just "fetch".
But if we remove those APIs from the window object, escaping the sandbox will no longer be enough to reach other APIs, and the dep will remain stuck with only "fetch"!

This is how you reach a wacky state where your app works just fine, but the real window object's dead inside πŸ₯²
We call the removal of the APIs and properties of the global object "scuttling" (coined by @kumavis_), and it actually works!

... in theory.

In reality, it's not so simple - we still skip scuttling of some specific properties.

But I will elaborate on that in the next thread πŸ˜‰
In the meantime, some resources:

* Original PR introducing scuttling to LavaMoat

* List of properties we DON'T scuttle at the moment (which I'll talk about in the next thread)

* SES Compartments by @agoric