如何 WASM 沙箱?

解读

面试官抛出“如何 WASM 沙箱?”并不是想听“把 Rust 编译成 .wasm 丢进浏览器就行”。在国内落地场景里,沙箱=可控、可审计、可隔离、可限权
考点集中在:

  1. 编译期如何把“不安全”的 Rust 代码关进笼子;
  2. 链接期如何生成单实例、无宿主符号泄露的模块;
  3. 运行时如何精细配额(CPU、内存、syscall)、如何做** capability-based 授权**;
  4. 国产合规要求:国密算法、等保 2.0 访问控制、信创环境(龙芯/鲲鹏 + 麒麟 V10)适配。
    一句话:让 .wasm 成为“不可逃逸、不可越权、不可耗尽宿主资源”的沙箱进程。

知识点

  1. Rust 侧

    • wasm32-wasi target:唯一真子集,禁用 std::threadstd::timestd::fs 等宿主敏感 API;
    • #![no_std] + alloc + 自定义 panic_handler:把标准库拿掉,杜绝隐式系统调用
    • wasm-bindgen 只导出最小接口,禁止 #[wasm_bindgen(start)],防止模块自启动;
    • 使用 wee_alloclol_alloc内存配额可感知的分配器;
    • build.rs 里用 cargo auditable 生成 SBOM,满足国内审计要求。
  2. 编译与链接

    • RUSTFLAGS="-C link-arg=--no-entry -C link-arg=--export-dynamic" 生成可重入、无 _start 的纯 reactor 模型;
    • wasm-opt -O4 --strip-producers --strip-debug 裁剪自定义段,防止泄露编译路径
    • wasm2wat 人工审计导入表,确保只有 wasi_snapshot_preview1 白名单函数
    • 若需国密,把 libsm 编译成 wasm32-wasi 静态库,禁用 x86_64 汇编路径
  3. 宿主侧(Rust 或 Go)

    • 选型:wasmtime ≥ 15.0(国内镜像源:rsproxy.cn),禁用 JIT 编译Config::strategy(Strategy::Cranelift) 即可,信创环境无 Intel CET 问题);
    • 资源配额:
      Store::limit_memory(8 * 64 * 1024) 限制 512 KiB;
      Store::limit_fuel(1_000_000) 配合 consume_fuel指令级计费
    • 系统调用过滤:用 wasmtime_wasi::WasiCtxBuilder 只挂接 random_getclock_time_get最小白名单fd_write 重定向到宿主管道,禁止直接写文件系统
    • capability 模型:把 wasmtime::InstancePre国产 RBAC 微服务对接,每个租户一个 Store,实例间内存零共享;
    • 热升级:利用 wasmtime::Module::serialize 预编译成 ELF 共享库,秒级冷启动 < 5 ms,满足金融行情场景。
  4. 国产合规加固

    • 等保 2.0:在宿主层加 seccomp-bpfselinux 双保险,即便 wasmtime 被 RCE 也无法提权
    • 国密 TLS:宿主与沙箱内分别使用 sm2/sm3/sm4双向证书校验,满足银保监文件要求;
    • 日志:把 fuel_consumedmemory_growth 打到Kafka -> 国密版 ELK,留存 ≥ 6 个月。

答案

“在 Rust 项目里,我通过四步实现 WASM 沙箱:
第一步,代码层wasm32-wasi target 并去掉标准库,只保留最小导出函数;自定义 #[global_allocator] 使用带配额回调的 wee_alloc,一旦超量立即 trap
第二步,编译期wasm-opt 裁剪自定义段,关闭所有调试符号;用 wasm2wat 人工审计,确保导入表只有 wasi_snapshot_preview1 中的 random_getclock_time_get 等 7 个安全函数。
第三步,宿主层wasmtime,关闭 JIT、开启 fuel 机制,内存上限 512 KiB,CPU 上限 100 万 fuel;系统调用通过 WasiCtxBuilder 白名单过滤,并把 fd_write 重定向到宿主管道,实现零文件系统访问。
第四步,合规加固:模块预编译成 ELF 共享库,启动时间 < 5 ms;宿主加 seccomp+selinux,日志实时入国密版 ELK,满足等保 2.0 与国密算法要求。
通过以上手段,.wasm 模块无法逃逸、无法越权、无法耗尽资源,形成生产级沙箱。”

拓展思考

  1. 多租户密度:一个 wasmtime::Engine 共享,每个租户一个 Store,内存池复用 MemoryPool 可把 1 GiB 内存切成 4 KiB 块,千实例常驻内存 < 50 MiB
  2. 异步宿主:把 wasmtime::Store 封装成 tokio::task,fuel 耗尽时 yield 回 Tokio,实现毫秒级抢占,解决长尾任务卡死问题。
  3. 信创环境:在龙芯 3C5000 + 麒麟 V10 上,wasmtime 需关闭 simdunwind 特性,compiler_builtins-mem 替代 LLVM memcpy,性能下降 15% 但可通过。
  4. 形式化验证:用 KLEE-wasm 对 .wasm 做符号执行,证明无整数溢出与越界访问,输出报告可直接提交给央行金融认证中心(CFCC)审计。