Design: For better security, remove mutable global variables

Created on 6 Jul 2018  Â·  12Comments  Â·  Source: WebAssembly/design

If security is an important goal, and I would think it should be critical in the Web, why not provide object-capabilities semantics, by removing mutable global variables altogether?

I think module instantiation, without them, might already provide every other protection that make ocaps semantics, if the instantiator decides which functions are passed to the module, thus being able to pass function that modify state or not.

Most helpful comment

@kephas, you might want to check out the actual Wasm design. Wasm modules are real modules that provide proper encapsulation. It already is the case that by default, nothing is exported. It's completely up to a module to decide what it exposes (individual globals, functions, tables, its memory). No other Wasm module nor JavaScript code can acquire a reference to something that a module does not export or otherwise chooses to hand out. Wasm is capability-safe.

All 12 comments

Can you elaborate on what properties mutable globals break, and what threat model you'd like to protect against?

The basic idea is that if there are mutable global variables, you cannot ever prevent two malicious agents from sharing authority.

The main threat adressed is when people integrate 3rd-party code in their own. If you provide ocaps semantics, they can do it completely safely; at least, the 3rd-party code will have no more authority to cause problems than what is explicitely given to it. (it is true with the 3rd-party code being the WebAssembly code, WRT the browser, or the 3rd-party code being another WebAssembly module, WRT to some main WebAssembly code)

For an example of this for Web client-side code, see Caja.

For an example of how impenetrable ocaps can make a system against actively harmful plugins, see the DARPA browser.

For a more in-depth explanation of the semantics, there is the article A Security Kernel Based on the Lambda-Calculus or the PhD thesis Robust Composition.

@kephas I am not experienced enough to comment on security, however I would like to clarify something:

WebAssembly globals are not the same as JavaScript globals: WebAssembly globals are local to that WebAssembly instance.

By default the globals are completely encapsulated inside of the WebAssembly instance and cannot be accessed by external code. A WebAssembly instance must explicitly choose to export a global in order for it to be visible to external code.

In addition, external code can only interact with WebAssembly globals via the imports/exports system (which is the same system that is used to import/export functions).

In other words, WebAssembly globals are quite similar to top-level variables which are defined/imported/exported in an ES6 module.

Does that change your perspective on the security of WebAssembly globals, or do you still foresee some potential issues?

There is something that I don't get in the docs: if I instantiate modules A, B and C, and B and C both import A, how do I get them access to A in case of dynamic linking?

Also, if A exports a global variable, will B and C get access to a shared reference when references are added post-MVP?

@kephas As far as I know, currently a wasm module cannot directly import from another wasm module, instead it must use JavaScript as an intermediary.

Eventually wasm modules will be able to directly import other modules, using paths similar to ES6 modules. e.g. they could import the foo global from ./path/to/bar.wasm. I imagine the security will be essentially the same as the security used for ES6 modules.

Assuming that A is instantiated only once, then yes I would expect B and C to get access to the same shared global. Though I would like to point out that B and C can also access all of the functions exported by A (and those functions can change state, so they're potentially just as dangerous as globals).

I'm not sure what will happen if a WebAssembly module is instantiated multiple times (whether each instance gets a fresh global, or whether all the instances share the same global). Perhaps somebody else can answer that question.

The WebAssembly spec itself is extremely agnostic toward how modules are instantiated. So you might have an easier time looking through the WebAssembly<->JS spec.

As far as I know, currently a wasm module cannot directly import from another wasm module, instead it must use JavaScript as an intermediary.

Yes, the module defers this to the embedder (typically JS). As far as the module is concerned, it just knows that something is providing the import, but it doesn't know where it came from.

I'm not sure what will happen if a WebAssembly module is instantiated multiple times (whether each instance gets a fresh global,

Yes, if the global is defined in the module and exported, then each instance creates its own global instance.

If we want to help developers make the Web more secure, I think the default should be that modules don't share mutable state, so that the default is to not make any channel available between malicious codes (or the ability for malicious code to mess with friendly code).

But the most expressive is when you can choose which mutable state you share. For example, if you want to let a WASM module render something visually, you would let that module import either a mutable buffer of the exact size where you want rendering to happen, or a function that has access to that buffer (and absolutely no other mutable state, and in the best of worlds, if at another time that module of another one must render at the same place visually, you don't let them access the same mutable buffer, because the modules might use the data to communicate).

So if I get it right, that is something that is entirely the responsibility of the _embedder_?

@kephas, you might want to check out the actual Wasm design. Wasm modules are real modules that provide proper encapsulation. It already is the case that by default, nothing is exported. It's completely up to a module to decide what it exposes (individual globals, functions, tables, its memory). No other Wasm module nor JavaScript code can acquire a reference to something that a module does not export or otherwise chooses to hand out. Wasm is capability-safe.

@rossberg Thanks for the explanation!

Do you know if that answer is intended to stay true as WebAssembly evolves, or is it an unintended consequence of the current design? (I know there are a lot of people itching to avoid writing JS themselves and have direct DOM access from non-JS WASM languages. Do you think people will accomplish that by using some common JS loader that loads and exports to the WASM a bunch of DOM adapters/references, or will WASM gain the ability to directly access the DOM or JS globals without an import?)

@AgentME It is currently possible to create small JS shims to use host operations (such as the DOM).

For example, that technique is used by stdweb and wasm-bindgen, which are two frameworks that allow you to write large complex (and fast!) webapps/websites entirely in Rust.

As for direct host bindings (without JS shims), there are proposals for adding them to wasm:

https://github.com/WebAssembly/reference-types
https://github.com/WebAssembly/host-bindings

@AgentME, absolutely intentional. Any future functionality for JS/DOM interaction will have to be accessed through imports one way or the other, e.g., via some system library. Not only to maintain the possibility of sandboxing, but also because core Wasm needs to remain independent of JS and the Web.

So there is still hope for a less insane web ecosystem…

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JimmyVV picture JimmyVV  Â·  4Comments

arunetm picture arunetm  Â·  7Comments

konsoletyper picture konsoletyper  Â·  6Comments

dpw picture dpw  Â·  3Comments

ghost picture ghost  Â·  7Comments