把本地 Python 关进 WASM 沙箱:先跑起来,再谈信任

2026-07-03 27 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

在本地运行一个陌生 Python 项目,风险不只是不小心 rm -rf。它可能读取 SSH key、扫描环境变量、改写你的源码目录,或者把私密文件发出去。这期播客讨论的核心问题很实际:能不能用 WASM 和 MicroPython 一类的运行时,把 Python 代码放进一个本地沙箱里执行,降低对宿主机的伤害面?

本地执行为什么危险

Python 的问题不在语言本身,而在默认权限太大。你在终端里运行:

python app.py

这段代码通常可以做这些事:

  • 读取当前用户可读的文件,比如 ~/.ssh/id_rsa.env、浏览器缓存目录。
  • 修改当前工作目录下的代码、测试数据、构建产物。
  • 访问网络,把机器上的信息发出去。
  • 调用系统命令,例如 subprocess.run(["curl", ...])

虚拟环境只能隔离依赖,不能隔离权限。venv 防止包版本打架,却不会阻止恶意脚本读你的家目录。要处理“不可信代码”,需要进程、文件系统、网络和系统调用层面的边界。

WASM 沙箱的吸引力

WASM 的价值在于它默认不是一个完整操作系统。一个 WASM 模块能访问什么,通常需要宿主显式授予。文件系统、环境变量、网络、时钟、随机数等能力都可以被设计成“按需开放”。

这也是用 WASM 跑 Python 的思路:把解释器本身编译到 WASM,比如 MicroPython 的 WASM 形态,或者在浏览器里使用 Pyodide 这类 CPython/WASM 运行时。用户提供的 Python 代码进入 WASM 解释器,而不是直接进入宿主机的 Python 进程。

边界大致变成这样:

untrusted code.py
        ↓
Python interpreter compiled to WASM
        ↓
WASM runtime / browser sandbox
        ↓
explicitly granted files, APIs, and I/O

这不是魔法防弹衣。它减少的是默认权限,真正安全还要看宿主 API 暴露了什么、是否允许网络、是否挂载真实目录、是否限制 CPU 和内存。

可以这样实践:用浏览器里的 WASM Python 做本地试验

如果你的目标是 MicroPython,可以把下面例子里的运行时替换成 MicroPython 的 WASM 构建;这里用 Pyodide 演示同类模式,因为它可以直接复制成一个 HTML 文件运行,能清楚看到“代码在 WASM Python 里执行,而不是直接碰宿主文件系统”。

创建 sandbox.html

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <title>Local Python WASM Sandbox</title>
    <style>
      body { font-family: system-ui, sans-serif; max-width: 900px; margin: 32px auto; }
      textarea { width: 100%; height: 220px; font-family: ui-monospace, monospace; }
      pre { background: #111827; color: #e5e7eb; padding: 16px; overflow: auto; }
      button { padding: 8px 14px; }
    </style>
  </head>
  <body>
    <h1>Local Python WASM Sandbox</h1>
    <textarea id="code">print("hello from wasm python")

try:
    open("/etc/passwd").read()
except Exception as exc:
    print(type(exc).__name__, exc)</textarea>
    <p><button id="run">Run</button></p>
    <pre id="out"></pre>

    <script src="https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.js"></script>
    <script>
      const out = document.querySelector("#out");
      const code = document.querySelector("#code");
      const run = document.querySelector("#run");

      let pyodideReady = loadPyodide({ stdout: append, stderr: append });

      function append(text) {
        out.textContent += text + "\n";
      }

      run.addEventListener("click", async () => {
        out.textContent = "";
        const pyodide = await pyodideReady;
        try {
          await pyodide.runPythonAsync(code.value);
        } catch (err) {
          append(String(err));
        }
      });
    </script>
  </body>
</html>

运行方式很朴素:

python3 -m http.server 8000

然后打开:

http://localhost:8000/sandbox.html

你可以改 textarea 里的代码做实验。它能运行 Python 逻辑,但不能像宿主机上的 python script.py 那样随便读取你的真实文件系统。这个例子适合用来验证产品交互、教学环境、代码片段演示,或者给用户提交的小段 Python 做低风险预览。

真要跑陌生代码,还要补几道闸

WASM 沙箱解决的是“默认能力太大”的问题,但生产环境不能只靠一句“用了 WASM”。可以按下面清单收紧:

  • 不挂载宿主目录,或只挂载一次性临时目录。
  • 默认关闭网络;确实需要网络时,通过代理白名单转发。
  • 给运行设置超时,避免 while True: pass 占满 CPU。
  • 限制内存,避免用户代码制造大对象拖垮页面或进程。
  • 捕获 stdout/stderr,不把宿主环境变量混进输出。
  • 把包安装能力当成高风险能力处理,不允许任意安装和导入本地扩展。

对于浏览器内沙箱,还要注意:浏览器提供的是页面级环境,不是服务器安全边界。用户可以改前端代码,所以不要把密钥、评分逻辑、付费权限判断放进前端沙箱里。

采用建议

如果你只是检查一个开源项目是否可信,最稳妥的做法仍然是读代码、锁网络、使用临时账号或容器。WASM/MicroPython 更适合“运行小段 Python 代码”这种场景:在线教程、插件规则、表达式求值、面向用户的脚本框。

判断是否值得引入 WASM 沙箱,可以问三个问题:

  • 代码是否来自不完全可信的用户或项目?
  • 是否只需要 Python 语言子集,而不是完整 CPython 生态?
  • 是否能接受文件、网络、系统调用能力被显式建模?

三个问题都接近“是”,WASM 沙箱就值得认真评估。它不会替你证明代码善良,但能把“本地随便跑”改成“给什么权限,才能碰什么东西”。


相关推荐