让 AI 执行代码时,沙箱应该怎么做?

深入安全实践对比约 6 分钟读完

一句话回答

模型生成的代码要按不可信代码对待:它可能被提示注入操纵,也可能只是写错了。要防的是读取敏感文件、访问内网、挖矿等资源滥用和破坏运行环境。做法分两层:按风险选择隔离级别(普通容器、gVisor 这类加强隔离的容器运行时、Firecracker 这类微虚拟机、WebAssembly,或者在用户浏览器里执行);无论选哪种,都要限制网络、文件系统、CPU、内存和运行时间,并且每次任务用一次性环境,用完即销毁。

详细解析

要防什么

风险 例子
读取敏感信息 读环境变量里的 API Key、宿主机的配置文件、其他用户的文件
访问内网 请求内部服务和数据库;请求云服务器的元数据地址(如 169.254.169.254)拿到临时凭证
资源滥用 挖矿、死循环、fork 炸弹、写满磁盘、对外发起攻击
破坏环境 删除文件、修改系统配置,影响后续任务
逃逸 利用内核或运行时的漏洞,从沙箱进入宿主机

隔离方案对比

方案 原理 隔离强度 代价和限制 适合
普通容器(如 Docker) 命名空间 + cgroups,和宿主机共享内核 较弱,内核漏洞可能导致逃逸 启动快、开销小 单租户、低风险的任务,必须配合下面的加固措施
加强隔离的容器运行时(如 gVisor) 在用户态实现内核接口,拦截容器的系统调用,减少对宿主机内核的直接调用 较强 系统调用密集的程序会变慢,个别系统调用不支持 多租户运行不可信代码,又想保留容器的使用方式
微虚拟机(如 Firecracker) 每个沙箱是一个轻量虚拟机,有独立的内核,靠硬件虚拟化隔离 强 宿主机要支持 KVM,运维复杂度高 多租户的代码执行平台
WebAssembly 在 Wasm 运行时里执行,默认没有文件和网络能力,由宿主显式授予 强,取决于运行时的实现 语言和库受限,很多原生扩展用不了 插件、简单的计算逻辑
用户浏览器 代码在用户自己的浏览器里执行,如用 Pyodide 运行 Python 对服务端没有风险 受用户设备性能限制;要和页面隔离 数据分析、教学演示等轻量场景

在浏览器里执行时,代码要放进独立域名、带 sandbox 属性的 iframe 里运行,否则它能读到页面的 Cookie、存储和 DOM。

无论哪种方案,都要限制这些

  • 网络:默认禁止出网。确实需要时(如安装依赖)走代理,按域名白名单放行;禁止访问内网网段和云元数据地址
  • 文件系统:根文件系统只读,只挂载本次任务的工作目录;不挂载宿主机的其他目录和 Docker 的 socket;不把密钥放进环境变量
  • 资源:限制 CPU、内存、进程数和磁盘写入量;设置墙钟超时,超时由外部直接销毁整个沙箱
  • 权限:用非 root 用户运行,去掉所有 Linux capabilities,禁止提权
  • 一次性环境:每个任务或会话用一个新沙箱,结束后销毁,避免任务之间互相影响和数据残留。可以维护预热的沙箱池来降低启动延迟

结果和日志

  • 限制输出大小(标准输出、生成的文件),超出就截断
  • 执行结果会回到模型的上下文。如果代码读取了外部数据,结果里可能夹带注入内容,要按不可信数据处理
  • 生成的文件给用户下载前检查类型和大小;要展示的 HTML 结果放进独立域名的沙箱 iframe,见输出渲染的安全风险
  • 记录执行的代码、耗时、资源用量和退出原因,便于审计和发现滥用

代码示例

用 Docker 运行一段 Python 的加固参数(多租户场景要配合 gVisor 或微虚拟机)。写成 bash 数组,方便逐行注释:

Shell
args=(
  --rm
  --runtime=runsc                         # 使用 gVisor,需要先安装并在 Docker 里注册
  --network none                          # 禁止联网
  --read-only                             # 根文件系统只读
  --tmpfs /tmp:rw,noexec,nosuid,size=64m  # 只有 /tmp 可写,最多 64 MB
  -v /srv/jobs/42:/workspace:ro           # 只挂载本次任务的代码,只读
  --memory 512m --memory-swap 512m        # 内存上限,两者相等表示不使用 swap
  --cpus 1
  --pids-limit 64                         # 进程数上限,防 fork 炸弹
  --user 65534:65534                      # 以 nobody 身份运行
  --cap-drop ALL
  --security-opt no-new-privileges
)
docker run "${args[@]}" python:3.12-slim timeout -s KILL 10 python /workspace/main.py
# 容器里的 timeout 只是第一层:调度程序在外部也要计时,超时后强制删除容器

面试官可能追问

为什么普通的 Docker 容器不够安全?

容器和宿主机共享同一个内核,隔离靠的是内核提供的命名空间和 cgroups,内核本身有漏洞时,容器里的代码就有机会逃逸。再加上常见的错误配置(特权模式、挂载 Docker 的 socket、以 root 运行),普通容器不适合作为多租户运行不可信代码的唯一边界。

代码需要安装第三方包或者访问网络怎么办?

优先把常用的包预装进镜像。确实需要在线安装时,只允许访问内部的包镜像源。需要访问外网时走出口代理,按域名白名单放行并记录日志,同时拦截内网网段和元数据地址。要清楚:只要能联网,数据就有被带出去的可能。

像 Jupyter 那样多次执行、共享变量的会话,沙箱怎么设计?

每个用户会话分配一个独立的沙箱并保持存活,设置空闲超时和最长存活时间,到期回收。需要保留的文件写到对象存储,沙箱被回收后可以恢复文件,但内存里的变量会丢失,要在界面上告诉用户。无论如何不能让多个用户共用一个沙箱。

沙箱启动慢,影响体验怎么办?

维护一个预先创建好的沙箱池,请求来了直接分配,用完销毁再补充新的;精简镜像;微虚拟机可以从快照恢复来加快启动。不能为了速度在用户之间复用沙箱。

易错点

  • 为了方便,把 API Key、数据库密码放进沙箱的环境变量
  • 只禁止访问公网,忘了内网和云元数据地址
  • 只在沙箱内部设置超时:沙箱里的进程可以绕过它,超时必须由外部强制执行
  • 多个用户或多个任务复用同一个沙箱

AI 模拟面试官

用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮

登录后就可以和 AI 面试官对练,面试记录也会保存下来。登录

这道题你掌握了吗?

选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。

学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。