sandbox

Type Object
Mandatory No
Manifest version 2 or higher
Example
json
"sandbox": {
 "pages": ["sandbox.html"]
}

Use the sandbox key to designate one or more of an extension's pages as sandboxed pages.

Sandboxed pages are loaded with a unique, opaque origin, instead of the extension's usual moz-extension:// origin. As a result:

  • Sandboxed pages can't access WebExtension APIs. The browser and chrome global objects are not available.
  • Sandboxed pages can't access, and can't be accessed by, other pages in the extension, except by using Window.postMessage().

A sandboxed page can be given a more permissive content security policy (CSP) than the rest of the extension. This includes a CSP that permits eval() and similar constructs that are blocked by an extension's default content security policy. Because a sandboxed page can't use WebExtension APIs or reach the rest of the extension directly, this can be done without weakening the security of the extension as a whole.

This makes the sandbox key useful for including a third-party library that relies on eval() or new Function(), such as some templating engines: load the library in a sandboxed page, and use postMessage() to send it data from, and return results to, the rest of the extension.

Manifest V2 syntax

In Manifest V2, sandbox is an object with these properties:

Name Type Description
pages Array of String Required. A list of paths, relative to manifest.json, to the extension's sandboxed pages.
content_security_policy String Optional. The content security policy applied to the sandboxed pages. See Content security policy for sandboxed pages.
json
"sandbox": {
  "pages": ["sandbox.html"],
  "content_security_policy": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';"
}

Manifest V3 syntax

In Manifest V3, sandbox only supports the pages property:

json
"sandbox": {
  "pages": ["sandbox.html"]
}

The content security policy for sandboxed pages is instead set using the sandbox property of the content_security_policy key:

json
"content_security_policy": {
  "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';"
}

Content security policy for sandboxed pages

If a policy is not supplied, sandboxed pages get this default content security policy:

sandbox allow-scripts; script-src 'self';

This isolates a sandboxed page from the rest of the extension, but doesn't allow eval() or similar constructs. To permit these, include 'unsafe-eval' (or 'wasm-unsafe-eval' for WebAssembly) in the script-src directive of a custom policy.

Note: Chrome has a more permissive default sandboxed pages CSP: sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';.

Any custom policy supplied for sandboxed pages must meet these requirements:

  • It must include the sandbox directive.
  • The sandbox directive must not include the allow-same-origin keyword. Allowing this would give the page access to the rest of the extension's origin, defeating the purpose of sandboxing it.

Example

This example loads a third-party templating library that uses eval()-like constructs into a sandboxed page, and uses it from a popup.

manifest.json:

json
{
  "manifest_version": 3,
  "name": "Sandbox example",
  "version": "1.0",
  "action": {
    "default_popup": "popup.html"
  },
  "sandbox": {
    "pages": ["sandbox.html"]
  },
  "content_security_policy": {
    "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';"
  }
}

sandbox.html:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <script src="templating-library.js"></script>
    <script src="sandbox.js"></script>
  </head>
  <body></body>
</html>

sandbox.js listens for messages from the popup, renders a template using the sandboxed library, and posts the result back:

js
window.addEventListener("message", (event) => {
  const { template, context } = event.data;
  const render = TemplatingLibrary.compile(template);
  event.source.postMessage({ result: render(context) }, event.origin);
});

popup.html embeds the sandboxed page in a hidden iframe:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <script src="popup.js"></script>
  </head>
  <body>
    <iframe id="sandbox" src="sandbox.html" hidden></iframe>
  </body>
</html>

popup.js sends data to the sandboxed page and handles the result:

js
const sandbox = document.getElementById("sandbox");

sandbox.addEventListener("load", () => {
  sandbox.contentWindow.postMessage(
    { template: "Hello, unsupported templ: name!", context: { name: "world" } },
    "*",
  );
});

window.addEventListener("message", (event) => {
  console.log(event.data.result);
});

Browser compatibility