Running Software Securely with WebAssembly?
First you have to invent the Universe!

Gabor Javorszky, November 28th 2024

ISC2 Thames Valley Chapter, UK, online

Who am I?

Senior Engineer, Software at F5 Networks, NGINX team. This presentation is in a personal capacity.

Previously at a startup called Suborbital (bought by F5). We built a SaaS that allowed our customers to run their customers' untrusted code safely within their or our infrastructure using WebAssembly.

Now in the NGINX team working on Unit, a universal web server, that allows users to run wasm components within the wasi-http world.

What am I going to talk about?

What is wasm?

What is untrusted code?

What is “securely” in this context?

What does the Universe have to do with this?

What is wasmtime, WASI, and the bytecode alliance?

What is wasm?

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

from webassembly.org

What is wasm?

Source languages to wasm

Go

.NET

.Rust

C

C++

Kotlin

AssemblyScript

D

Zig

Haskell

Grain

.NET

⇒ .wasm

What is wasm?

CPU architecture

x86

amd64

aarch64

arm

RISC-V

MIPS

⇐ .wasm

How do you get wasm?

You write it yourself. Don't actually do this. At this point you're manually shuffling bytes. Specification and demo.

source code of a wasm component in text

You take a pre compiled wasm component and run it yourself


								$ wasmtime serve -Scli \
								  --dir /path/to/local/directory::/guest/directory \
								  your_component.wasm 
							

You write your software in a language, like Rust, and then compile it down to wasm. I'm going to focus on this.

How does WebAssembly work?

As WebAssembly components are compiled binary artifacts, they need something to run them. They are called runtimes. Several of them exist:

  • wasmtime (safest, written in Rust)
  • wasmer (fast, not safe)
  • wazero (written purely in Go)

Wasmtime

We've chosen wasmtime to run the components we're handed for a number of reasons, but security is the absolute top one.

Many mitigations against accidentally compromising a system.

Security with wasmtime

Formal verification using the Coq project and Isabelle.

Continuous (24/7) fuzz testing of the entire codebase.

Bespoke Spectre mitigations both in the WebAssembly spec, and in wasmtime itself.

The bytecodealliance has an article on their security practices: Security and Correctness in wasmtime.

Security features

WebAssembly programs are sandboxed and isolated from one another and from the host, so they can’t read or write external regions of memory, transfer control to arbitrary code in the process, or freely access the network and filesystem.

From the blog post linked.

Security features

... But these security properties only hold true if the WebAssembly runtime’s implementation is correct. This article will highlight the ways we are ensuring correctness in the Wasmtime WebAssembly runtime and in its compiler, Cranelift.

From the blog post linked.

Security features

Because each wasm component gets run in a runtime, that runtime is responsible for providing resources to the components. Those resources include:

  • Interfaces - imports and exports
  • Environment variables
  • Filesystem

The combination of these is called a world.

Interfaces

Provides

  • system time
  • http comm functions

Wants

  • system time
  • http comm functions
  • filesystem
  • environment variables

Interfaces

Just because a runtime provides an interface for a set of functionality, doesn't mean the component will get real data.

As an example: the world might say "you can call a function to get the system time", but the world might also not have access to system time.

Will return a mock value, 1970 January 1st usually.

Filesystem access

Files need to be explicitly mounted into the runtime, much like in Docker.

"You have access to these files and folders at these virtual locations."

What is WASI?

WebAssembly System Interface

What is WASI?

...group of standards-track API specifications for software compiled to the [...] Wasm standard. WASI is designed to provide a secure standard interface for applications that can be compiled to Wasm from any language...

What is WASI?

A set of interfaces for common use cases: HTTP, filesystem, I/O, clocks, random, sockets, CLI, and more, provided as WIT world files.

See WASI.dev for more info.

Nothing by default

Components do not have access to anything on the host system by default. Everything needs to be very explicitly enabled and made available to them (exported).

You can't misuse something that doesn't exist.

Nothing by default

If you have a provide-nothing runtime, and a component that needs functionality, you can compose that component with another one that will export the needed functions, returning mock data.

Nothing by default

Runtime provides

Nothing

Component needs

  • ⇒ system time
  • ⇒ function_A
  • ⇒ function_B

Nothing by default

Runtime provides

Nothing

Bridge component

Imports nothing

Exports:

  • function for system time ⇒
  • function_A ⇒
  • function_B ⇒

Component needs

  • ⇒ system time
  • ⇒ function_A
  • ⇒ function_B

What can you use it for?

Client hands you a .wasm component that uses your specific world, or any of the WASI worlds.

You can then run it with wasmtime without worrying that the component will do anything unexpected.

Failure modes

  • Unknown unknowns
  • Bugs in the runtime (massively mitigated in wasmtime)
  • component bugs, like crashes internally

Resources and links

Resources and links

Resources and links

Q&A

Connect with me on LinkedIn, BlueSky, or my blog.

Source code for this presentation