Hardened JavaScript
Hardened JavaScript is a standards track mode for the JavaScript language for safe plugin systems and supply chain attack resistance. Hardening JavaScript improves a program’s integrity in the face of adversarial code in the same process.
Mechanisms
Hardened JavaScript has three features: Lockdown, Compartments, and Harden.
-
Hardening an object (
harden(object)
) freezes it and every other object reachable by visiting prototypes and properties, making it safe to share with multiple parties. The object (or “capability”) is tamper-proof but not immutable or pure. That means none of the parties that hold the object can alter its methods to eavesdrop or interfere with other parties. -
A Compartment (
new Compartment()
) is a sandbox with its own global object and evaluators (eval
,Function
,AsyncFunction
,Compartment
, andimport
). Unlike a same-originiframe
or V8vm
, all compartments have the same shared intrinsics likeArray
,Object
,Date
, andMath
. Because these are the same for every compartment, Hardened JavaScript enjoys compatibility with the vast majority of JavaScript. Programs that rely ondate instanceof Date
work the same. -
Lockdown patches up and hardens the shared intrinsics so they are safe to share with other parties and invulnerable to prototype pollution attacks. For example, after calling
lockdown()
, programs in any compartment can callnew Function(code)
to safely evaluate arbitrary code in the same compartment, butnew Function.prototype.constructor(code)
throws an error so it cannot evaluate code outside the compartment or access the trueglobalThis
.
Examples
Lockdown
Calling Lockdown enters the Hardened JavaScript mode. Thereafter, the shared intrinsics are frozen.
Lockdown does not erase any powerful objects from the initial global scope. Instead, Compartments give complete control over what powerful objects exist for client code.
Compartment
A compartment is a sandbox in which a program (a plugin or dependency)
can execute but not escape.
In the following example, we create a compartment endowed with a print()
function on globalThis
.
The
__options__
argument is a temporary accommodation forses
compatibility starting with version 1.6.0 and intended to become unnecessary in 2.0.0.
Harden
Harden gives all parties a foot to stand on to preserve the integrity of their objects and methods. Once hardened, an attacker can’t replace the methods of an object they share with another party.
Although the surface of the object (capability) is frozen, the capability still closes over the mutable counter. Hardening an object graph makes the surface immutable, but does not guarantee that methods are free of side effects.
Implementations
-
The npm package ses is a shim for Hardened JavaScript.
-
Moddable’s XS JavaScript engine implements Hardened JavaScript natively.
Applications
-
Agoric uses Hardened JavaScript to confine smart contracts.
-
Moddable uses Hardened JavaScript to confine programs on embedded devices.
-
MetaMask uses Hardened JavaScript to defend its supply chain for its web extension, at build time and run time with LavaMoat.
-
MetaMask also uses Hardened JavaScript to confine its Snaps plugins.
Boundaries
Hardened JavaScript does not protect the availability of a program. Any party in the same realm, regardless of compartment isolation, can drop into an infinite loop and prevent all other parties from making progress. Hardened JavaScript combines well with carefully chosen process or worker boundaries.
Hardened JavaScript protects confidentiality by default by omitting
timers and shared state between compartments.
Each compartment’s global object has only certain hardened, shared intrinsics
with other compartments, including Object
, Array
, Date
, and Math
, but
lockdown ensures that new Date()
, Date.now()
, and Math.random()
do not
work.
The compartment global object does not get any other properties from the host
(web browser or Node.js) like performance
.
Without these features, a confined program can’t use timing side channels
or observe that another party is drawing numbers from the Math
pseudo-random
number generator.
However, many confined programs will need timers and you (the host) can safely endow the compartments for a single party per process with timers, provided you keep no confidential information in the same process.