让 AI 执行代码时,沙箱应该怎么做?
一句话回答
模型生成的代码要按不可信代码对待:它可能被提示注入操纵,也可能只是写错了。要防的是读取敏感文件、访问内网、挖矿等资源滥用和破坏运行环境。做法分两层:按风险选择隔离级别(普通容器、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 数组,方便逐行注释:
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 轮
这道题你掌握了吗?
选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。
学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。