Join our Newsletter — 33% off our NHI Course
Home› Guides› WebAssembly Security Guide: Sandbox Model, Supply Chain and…
Guide Application Security

WebAssembly Security Guide: Sandbox Model, Supply Chain and Host Interfaces

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 26 October 2026 17 min read
On this page

WebAssembly is sold as "sandboxed", and that word leads teams to treat untrusted or memory-unsafe modules as safe by default, which they are not. The Wasm sandbox protects the host from the module, but it does not protect the module from its own bugs, and it says nothing about what the host chooses to hand the module through imports. This guide explains what the WebAssembly security model actually guarantees, where the real risk sits (inside linear memory, at the host interface, in the runtime and in the module supply chain), and how to deploy Wasm safely in the browser, on servers, in proxies and in plugin systems. It is written for application security engineers, platform teams embedding a Wasm runtime, and architects deciding whether Wasm is the right isolation boundary for a given job.

Key takeaways

  • The sandbox is one-directional. Wasm isolates the host from the module through bounds-checked linear memory, a protected call stack and type-checked indirect calls, but buffer overflows and use-after-free bugs inside a module's own linear memory still work.
  • Memory-unsafe source languages carry their bugs with them. C and C++ compiled to Wasm lose several native mitigations, so a heap overflow can be easier to exploit inside the module than on native code.
  • Imports are the real attack surface. A module can only do what the host gives it, so every host function, WASI capability and preopened directory is a privilege grant that needs the same review as an API permission.
  • The runtime is part of your trusted computing base. A compiler bug in the JIT or ahead-of-time code generator can break isolation silently, as Wasmtime's CVE-2023-26489 showed, so runtime patching is a security control, not housekeeping.
  • Resource limits are not automatic. Memory caps, CPU interruption (fuel or epochs), table limits and instance counts must be configured by the embedder, or a single module can exhaust a shared host.

What the WebAssembly security model guarantees

The WebAssembly specification's security design has two stated goals: protect users from buggy or malicious modules, and give developers primitives to build safe applications. Each module runs in a fault-isolated sandbox and can only reach the outside world through the APIs the embedding provides. In browsers the embedding adds the usual web policies, such as the same-origin policy.

Four properties do most of the work:

  • Linear memory isolation: a module's memory is a contiguous, zero-initialised byte array with a declared maximum size. Every load and store is checked against the region's bounds, and an out-of-bounds access traps. Address arithmetic does not wrap, which keeps bounds checking simple.
  • Protected call stack: return addresses and fixed-scope locals live outside linear memory and are addressed by index, so an overflow in linear memory cannot overwrite a return address. Classic stack smashing and return-oriented programming are therefore not possible.
  • Coarse control-flow integrity: direct calls must target declared functions, branches must target valid labels in the enclosing function, and indirect calls through a table are checked at run time against the expected function type signature. A mismatch traps.
  • No code injection: compiled code is immutable and not addressable from inside the module, so writable-and-executable memory does not exist and data cannot become code.

Traps (out-of-bounds access, signature mismatch, stack exhaustion, division by zero) stop execution immediately; the embedder decides whether to discard the instance.

What the sandbox does not protect

The same specification is explicit about the limits, and they matter more than the guarantees when you assess risk.

Not protectedWhat it means in practiceMitigation
Overflows inside linear memoryBounds are checked per memory region, not per object, so one buffer can overwrite its neighbours, including the C shadow stack that compilers place in linear memoryMemory-safe source language, bounds-checked containers, fuzzing the module
Pointer misuse (use-after-free, invalid dereference)Freed or stale pointers into linear memory still read and write valid bytes; nothing trapsMemory-safe language; allocator hardening; sanitiser builds in test
Function-level code reuseAn attacker who controls a function pointer can call any other function in the table with a matching type signatureEmscripten/Clang -fsanitize=cfi for C/C++ type-level checks; keep tables small
Race conditionsWith threads and shared memory, check-then-use bugs (TOCTOU) are possible; only atomics give ordering guaranteesCopy host-shared data before validating; avoid shared memory unless needed
Timing side channelsModules can still measure time and observe speculative execution effectsRuntime Spectre mitigations; cross-origin isolation in browsers; process isolation for hostile tenants

Lehmann, Kinder and Pradel's USENIX Security 2020 paper, Everything Old is New Again: Binary Security of WebAssembly, demonstrated these limits end to end. They showed that vulnerabilities in C and C++ carry over into Wasm binaries, that several bugs which native mitigations make hard to exploit become exploitable again, and that Wasm enables attacks with no close native equivalent, such as overwriting data the programmer assumed was constant and corrupting the heap through a stack overflow. They built working exploits on three Wasm platforms. The practical conclusion is that "compiled to Wasm" does not change the memory safety of the source. See the Memory Corruption Vulnerabilities Guide.

A compromised module cannot escape the sandbox by itself, but it can do anything its imports allow. If the module holds a session token, writes to a preopened directory or calls a host function that sends HTTP, an attacker who corrupts the module's memory inherits those abilities. The blast radius is defined by the host interface.

Deployment contexts and how the risk changes

Wasm runs in very different places, and the threat model changes with each.

ContextWho is untrustedMain risksKey controls
Browser (web app)The page's own code is trusted; third-party modules and user input are notMemory bugs reachable from input, third-party module tampering, cryptojacking, side channelsCSP with 'wasm-unsafe-eval', same-origin hosting or integrity checks, cross-origin isolation for threads
Server-side runtime (Wasmtime, WasmEdge, wazero, WAMR)Often the module itself (customer code, plugins)Runtime escape, resource exhaustion, over-broad WASI capabilitiesMinimal imports, resource limits, patching, process or VM isolation for hostile tenants
Proxy and gateway extensions (Proxy-Wasm in Envoy, NGINX, Apache Traffic Server)Third-party filters, sometimes internal teamsFilters see every request and header, including credentials; crashes take down the data planeSigned filter artefacts, code review, staged rollout, per-filter limits
Plugin systems in desktop or SaaS productsPlugin authors and marketplacesData exfiltration through granted host functions, malicious updatesCapability manifests, review, signing, revocation

For server-side and edge use, see the Serverless Security Guide and the Edge Computing Security Guide. When Wasm is used to run code generated by an AI agent, the same principles apply as in the AI Agent Sandboxing and Code Execution Guide.

Securing WebAssembly in the browser

  • Content Security Policy: if a page sends a CSP header and script-src does not include 'wasm-unsafe-eval', the browser blocks WebAssembly compilation and instantiation. Use 'wasm-unsafe-eval' rather than 'unsafe-eval': the narrower keyword allows Wasm only, while 'unsafe-eval' also re-enables JavaScript eval() and overrides the narrower setting. See the Content Security Policy Guide.
  • Serve with the correct MIME type: WebAssembly.instantiateStreaming() and compileStreaming() reject with a TypeError unless the response is application/wasm. Configure this on the origin and CDN rather than falling back to ArrayBuffer loading, which hides misconfiguration.
  • Control where modules come from: host .wasm files on your own origin, or verify a known hash before compiling when you must load from a third party. OWASP ASVS 5.0 V3.6.1 requires integrity verification for externally hosted client-side assets; treat Wasm the same way as third-party JavaScript. See the Client-Side Security Guide.
  • Threads need cross-origin isolation: a WebAssembly.Memory created with shared: true is backed by a SharedArrayBuffer, which browsers only allow to be shared between threads in a secure context that is cross-origin isolated using the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers. Browsers restricted shared memory in 2018 because of Spectre; cross-origin isolation is the mechanism that brought it back safely. Do not weaken COOP or COEP on the rest of the site to make a Wasm library work.
  • Keep secrets out of client modules: anything compiled into a module shipped to a browser can be extracted with standard disassembly tools such as the WebAssembly Binary Toolkit. Wasm is not obfuscation. API keys, signing keys and licence checks belong on the server.
  • Watch for cryptojacking: the MineSweeper study (ACM CCS 2018) of the Alexa top million sites found drive-by mining campaigns that used JavaScript and WebAssembly modules to mine in visitors' browsers, and showed that blocklists and CPU-usage heuristics alone were insufficient. Unexpected .wasm loads in your pages are a signal of client-side compromise (ATT&CK T1496.001 Compute Hijacking).

Embedding a runtime safely: the host interface

When you run modules on your own servers, you are writing the security policy. WASI is designed so that a module "starts with no ambient authority and can only do what the host explicitly grants". The embedder's job is to grant as little as possible.

  1. Enumerate imports before instantiation. Inspect the module's import list and refuse to instantiate any module that imports functions outside an allowlist.
  2. Grant filesystem access by preopened directory only. WASI filesystem access is capability based: a module can only open paths under directories the host preopens. Preopen a dedicated, empty working directory per instance, read-only where possible, and never the host root or a directory containing configuration or credentials.
  3. Treat network capabilities as egress grants. If you expose sockets or outbound HTTP, route them through a host-side policy that restricts destinations, as you would for any workload. A module that can call arbitrary URLs is a server-side request forgery primitive. See the SSRF Prevention Guide.
  4. Validate every value crossing the boundary. Core Wasm passes integers, so host functions that receive a pointer and a length must bounds-check both against the module's memory before reading and must copy data out before validating it (a shared-memory guest can change it between check and use). Host functions are where most exploitable embedding bugs live.
  5. Prefer the Component Model for new designs. Components interact only through typed imports and exports defined in WIT (WebAssembly Interface Types), and a component may not export its memory. The Canonical ABI handles strings, lists and records, which removes much of the hand-written pointer-and-length code that causes host bugs, and the explicit component graph can be analysed to confirm, for example, that a business-logic component has no path to a component holding personal data.
  6. Never pass long-lived credentials into a module. If a module needs to call an API, give it a host function that makes the call with a short-lived, narrowly scoped token held by the host. The module never sees the secret. See the Secrets Management Guide.

Resource limits

ResourceRisk if unlimitedControl (Wasmtime naming as an example)
Linear memoryModule grows memory until the host runs outMaximum memory size per instance; a resource limiter that rejects growth
CPU timeInfinite loops block a worker thread or event loopFuel: deterministic instruction budget, higher overhead. Epoch interruption: host advances a counter on a timer, about 10% overhead per Wasmtime's documentation, but non-deterministic
Tables and instancesLarge tables or many instances consume host memoryTable element limits, instance count limits, pooling allocator caps
Native stackDeep recursion in host or guestMaximum Wasm stack size; host stack guard pages
OutputTerminal escape sequences or log injectionWasmtime translates ANSI escape sequences on terminal output; sanitise logs elsewhere

Choose fuel for reproducible cut-off points and epochs when overhead matters more. Whatever the runtime, test that a module with an infinite loop and a module that requests maximum memory are both stopped cleanly.

The runtime is part of your attack surface

A Wasm runtime is a compiler. Its code generator must emit a bounds check on every memory access and a type check on every indirect call; one miscompilation removes the sandbox for every module it runs. Wasmtime's own documentation describes the defence-in-depth it adds for that reason: a 2 GB guard region before each linear memory to catch sign-extension bugs in the Cranelift code generator, guard pages on native stacks, zeroing of memory, tables and instance data after an instance finishes, and Spectre mitigations on call_indirect table bounds checks, on br_table, and on memory bounds checks when dynamic memories are used.

The decision for an embedder is how much to rely on the runtime alone:

  • Trusted or first-party modules: in-process Wasm isolation is usually sufficient, combined with patching and minimal imports.
  • Customer or marketplace code: run instances in a separate, low-privilege process or container with no credentials in its environment, so a runtime escape lands in a box with nothing to steal. NIST SP 800-53 SC-39 (Process Isolation) is the control family; OWASP ASVS 5.0 V15.2.5 asks for sandboxing or containerisation around dangerous functionality. See the Container Runtime Security Guide.
  • Mutually hostile tenants with sensitive data: add a VM or microVM boundary per tenant and accept the cost, because timing side channels and speculative execution are not fully solved by any runtime. See the Side-Channel Attacks Guide.

Module supply chain

A .wasm file is a compiled binary. It is often built from a dependency tree you do not see (Rust crates, C libraries, Go modules) and distributed through npm packages, OCI registries or plugin marketplaces. The usual supply chain controls apply, with a few Wasm-specific points:

  • Build from source in your pipeline: prefer compiling modules yourself from pinned source over consuming prebuilt binaries inside packages, because a binary blob inside an npm package is rarely reviewed. Compromised packages are ATT&CK T1195.002 Compromise Software Supply Chain.
  • Record provenance and an SBOM: the SBOM must describe the source dependencies compiled into the module, not just the module file. See the SBOM Guide.
  • Sign and verify: sign modules and filters at build, verify the signature or digest before loading, and pin by digest when pulling from OCI registries.
  • Review the import list as part of code review: a dependency update that adds a new import (for example, a socket or environment access) is a privilege change and should fail the build until approved.

See the Software Supply Chain Security Guide for pipeline controls.

Choosing a source language

SourceMemory safety inside the moduleNotes
Rust (safe code)Strong; unsafe blocks and FFI to C are the exceptionsAudit unsafe and C dependencies. See the Rust Security Guide
Go, C#, Kotlin, AssemblyScript and other managed languagesManaged runtime or bounds checks prevent most memory corruptionLarger modules; the language runtime itself is compiled into the module
C and C++ (Emscripten, wasi-sdk)None beyond the region-level bounds check; overflows and use-after-free work inside linear memoryUse -fsanitize=cfi, hardened allocators, fuzzing. See the C and C++ Secure Coding Guide

Implementation roadmap

  1. Inventory: find every place Wasm runs: browser bundles (search builds for .wasm and WebAssembly.instantiate), server runtimes, proxy filters, and products that accept Wasm plugins. Record runtime name and version for each.
  2. Classify trust: for each, decide whether modules are first-party, third-party reviewed, or untrusted customer code. This sets the isolation tier.
  3. Lock down the host interface: write an import allowlist, remove unused WASI capabilities, preopen minimal directories, move credentials into host functions.
  4. Set resource limits and test them with deliberately hostile test modules (infinite loop, maximum memory growth, deep recursion) in a pre-production environment.
  5. Browser hardening: deploy CSP with 'wasm-unsafe-eval' in report-only mode first, check violation reports, then enforce. Confirm application/wasm on all origins.
  6. Pipeline: build modules from source, generate SBOM, sign, verify on load, fail builds on new imports.
  7. Rollout and rollback: ship new modules and filters behind a flag or canary ring; keep the previous signed module available so rollback is a pointer change, not a rebuild.
  8. Patch cadence: subscribe to runtime security advisories and treat a runtime sandbox CVE like a hypervisor CVE.

Detection and measurement

  • Log traps with context: trap type, module digest, tenant and input identifier. A burst of out-of-bounds or signature-mismatch traps from one module often means someone is probing it.
  • Log resource limit hits: fuel exhaustion, epoch deadline and memory-growth denials per module.
  • Monitor host function calls: rate and destination of outbound calls made on behalf of modules; alert on new destinations.

Useful metrics:

  • Percentage of modules loaded with a verified signature or digest (target 100%).
  • Median and maximum days from a runtime security advisory to patched production runtimes.
  • Number of distinct imports granted per module class, trending down.
  • Percentage of untrusted-module workloads running behind a second isolation boundary.
  • Trap and limit-hit rate per module version, compared before and after each release.

Common failure patterns

  • Treating the sandbox as memory safety: shipping a C image or document parser compiled to Wasm and assuming crafted input can do no harm. It can corrupt everything in that module's memory, including tokens and decisions.
  • Preopening too much: granting a plugin the application's whole data directory because it was convenient during development.
  • Secrets in client modules: embedding API keys in browser Wasm on the assumption that a binary is hard to read.
  • No CPU or memory limits: one customer function with a tight loop stalls a shared worker pool, an availability incident that looks like an outage, not an attack.
  • Unpatched runtimes: an old runtime pinned in a vendored dependency, missing sandbox-breaking fixes.

Lessons from incidents

The clearest public lesson is Wasmtime advisory GHSA-ff4p-7xrq-q5r8 (CVE-2023-26489, CVSS 9.9). A code generation bug in Cranelift on x86_64 let a malicious module read and write memory roughly 6 GB to 34 GB beyond the base of its linear memory, which the advisory describes as an arbitrary read and write. That memory could belong to other instances, for example under the pooling allocator, or to the embedder. Wasmtime 0.37.0 to 6.0.0 were affected and 6.0.1, 5.0.1 and 4.0.1 were fixed. The advisory also noted that exploitation could be silent. Three lessons follow: the runtime is part of the trusted computing base; multi-tenant hosts should not share a process with sensitive data; and you cannot rely on detection after the fact, so patch speed is the control.

Framework mapping

AreaNIST SP 800-53 Rev. 5NIST CSF 2.0Other
Isolating untrusted modulesSC-39 Process Isolation, SC-44 Detonation ChambersPR.PS-06OWASP ASVS 5.0 V15.2.5
Minimal host imports and capabilitiesCM-7 Least Functionality, AC-6 Least PrivilegePR.PS-06ISO/IEC 27001:2022 A.8.28 Secure coding
Module integrity and provenanceSI-7, SR-3PR.PS-02ATT&CK T1195.002; ASVS V3.6.1
Testing and fuzzing modulesSA-11, SA-15PR.PS-06CIS Controls v8.1 Control 16
Runtime monitoringSI-4DE.CM-09ATT&CK T1496.001, T1027

Practitioner checklist

  • Inventory every Wasm runtime and module source, with runtime versions, and assign each a trust tier.
  • Enforce an import allowlist and reject modules that request anything outside it.
  • Preopen only dedicated, minimal directories; never pass credentials or environment secrets into modules.
  • Configure memory, CPU (fuel or epoch), table and instance limits, and prove them with hostile test modules.
  • Run untrusted customer or marketplace modules behind a second isolation boundary (process, container or VM).
  • In browsers, use 'wasm-unsafe-eval' not 'unsafe-eval', serve application/wasm, and integrity-check third-party modules.
  • Build modules from pinned source, produce an SBOM, sign them, and verify the signature or digest before loading.
  • Prefer memory-safe source languages; for C and C++, fuzz, use CFI builds and hardened allocators.
  • Subscribe to runtime advisories and patch sandbox-breaking issues on an emergency timeline.
  • Log traps, limit hits and host-function egress per module digest and review them.

Standards and references

Related NHI Mgmt Group resources: Memory-Safe Languages Guide · Content Security Policy Guide · Software Supply Chain Security Guide · AI Agent Sandboxing and Code Execution Guide · Game Security and Anti-Cheat Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 26 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org