Jetpack Security Model

Features

Jetpack is designed to allow casual coders and experienced developers
to create useful yet secure functionality that acts on behalf of a
user agent (i.e., browser).

By "casual coders" we mean those who know the basics of JavaScript,
but aren't previously equipped with knowledge about best
practices. They are often self-described as "cut and paste" coders;
sometimes they don't even describe themselves as coders at all.

By catering to this kind of developer, the Jetpack Platform needs to
protect developers from their own inexperience: put simply, it should
be easy to "do the right thing" where security is concerned, and
harder to do things that are considered insecure (though not
necessarily impossible, since this hampers generativity).

Sandboxing

All Jetpacks are executed in their own sandbox and obey the Principle
of Least Authority (POLA). That is, by default, their code is
executed in a sandbox that doesn't have access to do anything harmful
to the user, be it destroying or stealing valuable information.

It's only with the user's consent—or, more preferably, the consent of
a trusted security advisor, such as an AMO reviewer—that a Jetpack is
executed with the privileges that the user or advisor grants to it.
The suggested specification of privileges provided by the Jetpack, along with other non-security-related metadata, is called its manifest.

After the point of execution, it's not possible for a Jetpack to
reduce or escalate its privileges, and anything it tries to do that
violates its privileges will result in a helpful security error.

Note that it's possible for a Jetpack to also contain its own sandboxes; for more information, see the Privilege Separation section.

Implementation Notes

Each Jetpack will run in its own Components.utils.Sandbox object with a content principal.

Dependency Injection

The manifest is essentially used as a high-level specification that guides the injection of dependencies into the Jetpack's global environment.

For instance, if the manifest doesn't specify that the Jetpack needs access to the network, then the XMLHttpRequest object won't be inserted into the Jetpack's global scope—or perhaps a "dummy" object that raises a helpful security error when accessed will be injected in its place.

Alternatively, if the manifest specifies that the Jetpack only needs to communicate with foo.com, then XMLHttpRequest will be injected but attenuated such that connections to domains other than foo.com will fail.

Implementation Notes

Injected functionality will be protected by Chrome Object Wrappers. These secure membranes ensure injection can't lead to privilege escalation.

In addition to injecting objects into the Jetpack's global scope, privileged code can also inject objects into the Jetpack's CommonJS SecurableModule namespace. For instance, an alternative to an XMLHttpRequest global might be a "network" module loaded via a call to require("network"), which returns an object that has XMLHttpRequest and other network capabilities attached to it.

Libraries

Code reuse is a key to a vibrant development ecosystem, but it must
also be done with care lest the user's computer be open to
attack. Jetpacks can leverage the CommonJS SecurableModule standard to
load packages of CommonJS code; these libraries execute in a private
scope, but with the same capabilities as their parent Jetpack.

This naturally means that the more powerful a Jetpack is, the more of a threat third-party libraries pose to the user's system. As this becomes the case, library authors are encouraged to use Code Signing to enable their code to be used in highly-privileged contexts, and Jetpack authors are encouraged to use the platform's Privilege Separation functionality to mitigate risk. The security manifest further allows the platform to deterministically recognize when a Jetpack is at risk, and advise the Jetpack author to secure their code before distributing it.

As per the CommonJS standard, a SecurableModule can be loaded via a
call to the global require() function.

Implementation Notes

Libraries will be loaded in SecurableModules with content principals (see JEP 25).

Superpowers

Superpowers are like Jetpack's analog to "native libraries" on other
platforms: like traditional XULRunner extensions, they have full
access to the Mozilla platform and its accompanying technologies such
as XPCOM and XUL, but they are entrusted to expose a small subset of
it to the importing Jetpack via dependency injection, perhaps
attenuating it as per manifest metadata. This is useful for
e.g. traditional addon developers who want to convert their extension
to a Jetpack, but require a feature or two that the Jetpack platform
doesn't yet provide.

Implementation Notes

A Superpower is actually implemented using a SecurableModule with a system principal that defines a standard function or class on its exports object. This function or class is responsible for injecting its functionality into a Jetpack. The Superpower's module runs inside a Cuddlefish runtime (see JEP 28), and as such, the Superpower is free to use other privileged SecurableModules provided either by Cuddlefish, itself, or a third party.

Code Signing

Superpowers and other code that executes in a highly-privileged space
are encouraged to be signed by a trusted authority such as AMO. This
ensures the integrity of the code and means that end-users will be
presented with a much friendlier user interface when trying to
install the containing Jetpack.

The process for code signing is intended to be as unburdensome as
possible. Ideally, adding a signature for code should consist of
adding a property to a manifest file or a specially-formatted comment
to the top of a script.

Privilege Separation

Jetpacks provide a mechanism called "Sandboxes" that allow the Jetpack to
embed "sub-Jetpacks" within itself, each with its own privilege
manifest. The Jetpack can communicate with its Sandboxes via a simple
JSON-based message-passing protocol.

The concept of Sandboxes is analogous to the privilege separation functionality many operating systems provide, whereby one highly-privileged process may fork into two separate processes, one of which drops its privileges, and the two communicate via a pipe or socket pair. For examples, see Wikipedia's article on privilege separation.

It's expected that Sandboxes won't need to be used by the majority of Jetpacks: they're essentially a mechanism that allows complex Jetpacks which require lots of capabilities to be able to segment their functionality into separate sandboxes.

Note that because the security characteristics of Sandboxes are specified statically before run-time, it's entirely possible for the main Jetpack to have no privileges, and instead simply facilitate communication between separate semi-privileged Sandboxes. For instance, one Sandbox could be responsible for interacting with the filesystem, while another could be responsible for interacting with the network, and so forth.

Sandboxes all have names, and are accessible through the jetpack.sandboxes
namespace.

Implementation Notes

Each Sandbox will run in its own Components.utils.Sandbox object with a content principal.

Capability Inference

For pedagogical purproses and to lower the barrier to entry for developers as much as possible, small Jetpacks do not require authors to create explicit
manifests. Instead, the Jetpack Platform performs basic static
analysis on the Jetpack's source code to infer its manifest based on
what API functions it appears to be using. This
automatically-generated manifest is then presented to the end-user or
advisor for authorization before the Jetpack executes.

That said, any third-party Libraries or Sandboxes linked to by Jetpacks
will need to be expressly given privileges in a manifest; this is done
to ensure that the Jetpack author is aware of what privileges they are
giving the third-party code. It's also intended as a kind of
explicit contract between the user and author of third-party code.

Helpful Errors

The term "helpful" is used to qualify the term "security error" in this document because it's expected that Jetpack authors will inadvertently encounter security errors when developing their own Jetpacks from time to time, and they will need guidance on how to change their code or their manifest to resolve the error, as well as information on how such a change affects the security of their Jetpack.

Therefore, instead of raising cryptic errors like "Security manager vetoed action", the text of the error should include specific information on why the action violated a security policy and what the developer can do about it.

Code Updating

The exact criteria for how transparent code updates are, and in what cases they are even allowed to occur, is still in flux. The following represents some issues to be considered.

As long as code is signed, updating it should be transparent, as it both relieves the user of the burden of having to authorize trusted code updates and also ensures that they're on the version of their software with the most recent security fixes.

However, if the code isn't signed, and has medium to high privileges, users may need to provide permission to update the code; indeed, we may even need to prohibit code updating on such Jetpacks over non-HTTPS protocols due to the danger of man-in-the-middle attacks, as Firefox's current extension update mechanism does.

Social Factors

In general, Jetpack manifests aren't intended to be read by non-technical end-users. Rather, they're intended to be read by reasonably technically experienced AMO reviewers and other trusted advisors—individuals who, for example, know what DNS and filesystems are but not necessarily what an XPCNativeWrapper or a security principal is.

This audience is expected to compare the manifest with whatever the Jetpack purports to do, as well as take into account any additional social and technical factors, and make a decision about the potential legitimacy of the Jetpack. This decision is then used to advise non-technical users on whether they should trust the Jetpack. This means that if a trusted authority has reviewed the Jetpack, they will sign its code, and end-users will be presented with a relatively benign user interface when installing the software.

However, without even a cursory review of a Jetpack by trusted human beings, the most the Jetpack Platform can do is present the user with some idea of the risk involved in installing the Jetpack. Jetpacks which require high privileges and aren't signed by a trusted authority will thus cause the platform to present the user with a dire warning, and will probably require them to do something fairly cumbersome during installation, e.g. make them type the words "I AGREE TO EXPOSE MY PERSONAL DATA TO THEFT OR DESTRUCTION BY THIS JETPACK", or drag an icon of a syringe filled with murky fluid over an icon of a human being with their inner elbow exposed. Jetpacks which require lower privileges, however, will present gradually friendlier user interfaces prior to installation.

Sample Code

Jetpack authors can bundle their Jetpack in a zip file, in which case
a manifest can be placed in a manifest.json file. Alternatively,
however, the Jetpack can be a single script that begins with a call to
jetpack.manifest.set(), like so:

jetpack.manifest.set({
// Some non-security-related metadata about the Jetpack.
title: "Atul's Twenty-Fourth Jetpack",
author: "Atul Varma",
capabilities: {
// Note that these capabilities are in addition to any
// capabilities we infer from static analysis of the Jetpack code.
network: {domains: ["toolness.com"]},
},
// Superpowers import functionality from chrome-space (i.e., the Mozilla
// platform) to the Jetpack that aren't yet available in the
// Jetpack platform. They generally add things to the jetpack
// namespace.
superpowers: ["http://foo.com/http-listener.zip"],
// These are URLS to CommonJS modules or archives of CommonJS modules.
// They are all given the same privileges as the Jetpack, and have
// access to the same 'jetpack' namespace.
libraries: ["http://bar.com/blargy.zip",
"http://bar.com/jquery.js"],
// Sandboxes are "sub-Jetpacks" that the Jetpack may communicate with
// via a postMessage-like interface. They get their own sandbox and
// have their own capabilities. They're accessible via
// jetpack.sandboxes.<name>. Unlike the main Jetpack, no
// capability inference is performed on them, so the capabilities
// specified in this manifest are the only ones they get.
sandboxes: {
blah: {url: "http://toolness.com/md5.js",
// Don't give it any capabilities!
capabilities: {}}
}
});

The above manifest distinguishes between Superpowers, Libraries, and Sandboxes—rather than lumping them all into one category, which is easier to write—because each type has different security characteristics that are important for Jetpack authors to think about and be aware of. Superpowers represent code that the Jetpack author trusts as much as the Mozilla platform itself, possibly even more than their own code; Libraries represent code that the Jetpack author trusts at least as much as their own code; and Sandboxes represent code that the author trusts more, less, or just as much as their own, depending on how the Sandboxes are configured and used.