大模型Agent最强大的能力之一,就是可以根据任务动态生成代码并交给运行时执行。无论是数据分析、自动化运维还是批量文件处理,这种“模型写代码、沙盒跑代码”的模式都极大拓展了应用边界。但问题也很现实:模型生成的代码本质上是不可信输入,一旦直接跑在宿主机上,一个被注入的恶意提示词就可能让它读取数据库密钥、外传文件,甚至删除整个目录。Agent安全沙盒要解决的,就是把这段“来历不明”的代码关进一个受控的笼子里执行。

为什么Agent代码执行必须放在沙盒里
传统应用中的代码由开发者编写并经过代码审查,而Agent生成的代码来源是模型输出,它至少面临三类独特威胁。第一是提示注入导致的恶意代码:攻击者可能在网页内容、文档或用户输入中埋入指令,诱导模型生成带有破坏性或窃取行为的脚本。第二是能力滥用:即使模型没有恶意,一段写错的递归脚本也可能耗尽CPU或内存,拖垮整个服务。第三是数据泄露通道:执行环境往往持有任务上下文,代码里一个HTTP请求就能把敏感数据发到外部服务器。
因此沙盒设计的目标可以概括为四点:代码只能访问明确授权的资源;执行过程有时间与资源上限;所有输入输出可被审计;即使代码失控,影响范围也被限制在沙盒内部。这四点分别对应隔离、限额、审计、可回滚四层防线,缺一不可。很多团队只做了第一层隔离就上线,结果在资源耗尽和数据外传上栽了跟头,这个教训值得记住。
三类主流隔离方案的技术原理与取舍
第一类是基于OS级虚拟化的容器隔离,典型代表是Docker加上gVisor或Kata Containers。它通过namespace实现文件系统、进程、网络的隔离,通过cgroups控制CPU、内存限额。优点是隔离粒度全面,支持任意语言;缺点是镜像体积与启动延迟,普通Docker容器每秒可以启动数十个,但在高并发Agent场景下仍是瓶颈,gVisor进一步牺牲约10%到30%的性能换取更强的内核攻击面收敛。
第二类是进程级沙盒,比如Linux上的bubblewrap、Firejail,或者直接组合seccomp与Landlock内核特性。它不创建完整容器,只是给子进程套上一层系统调用过滤器与挂载命名空间。启动开销接近fork,非常适合“每个任务一个短命进程”的模式。局限在于隔离边界依赖配置正确性,一旦过滤规则遗漏某个syscall,就可能留下逃逸缺口。
第三类是语言级沙箱,例如Python受限执行器、WebAssembly运行时(Wasmtime、WASI)。把不可信代码编译到Wasm再执行,天然没有文件系统和网络能力,必须通过显式导入的宿主函数访问外部资源。这种方式隔离最强、启动最快,但要求代码可移植到Wasm生态,对依赖C扩展的Python库支持有限。工程实践中常见的组合是:轻量任务走Wasm,重量级科学计算走容器,中间地带走进程级沙盒。
文件系统、网络、资源三个维度的隔离实操
文件系统隔离的核心原则是最小化挂载。沙盒内只挂载工作目录,且建议使用tmpfs或overlay的可写层,任务结束即销毁。宿主机敏感路径如/etc、用户home目录、云凭证目录绝对不能出现在沙盒的挂载表中。下面是一个用Docker Python SDK实现的简化执行器,展示了超时、内存限制、网络禁用与只读根文件系统的组合:
import docker
client = docker.from_env()
def run_in_sandbox(code: str, timeout: int = 30) -> tuple[int, str]:
container = client.containers.run(
image="sandbox-python:3.11-slim",
command=["python", "-c", code],
# 只读根文件系统,防止持久化篡改
read_only=True,
# 内存与CPU限额,防止资源耗尽
mem_limit="256m",
nano_cpus=500_000_000, # 0.5 核
# 禁用全部网络能力
network_disabled=True,
# 挂载临时可写目录
tmpfs={"/tmp": "size=64m,noexec"},
# 无额外能力、禁止提权
cap_drop=["ALL"],
security_opt=["no-new-privileges:true"],
detach=True,
)
try:
result = container.wait(timeout=timeout)
stdout = container.logs().decode("utf-8", errors="replace")
return result["StatusCode"], stdout
finally:
container.remove(force=True)
网络隔离需要区分任务类型。绝大多数代码执行任务不需要出网,直接network_disabled即可;确需访问外部API的任务,应该走代理白名单模式,由沙盒外的代理服务校验目标域名后再转发。切忌简单开放全部出网再靠事后审计补救,外传往往在毫秒级完成,事后追溯为时已晚。
资源限额方面,除了CPU和内存,还要注意进程数限制(pids-limit)、输出体积限制(logs过长会撑爆日志系统)以及任务级超时。超时控制建议做两级:容器wait一层,外层再有一个看门狗强制kill,避免容器runtime本身异常导致任务悬挂。
输出审计与逃逸防护的常见坑点
审计方面,务必把沙盒内stdout/stderr、退出码、文件系统变更全部落盘。对输出做两类检查:一是敏感模式扫描,比如base64编码的长字符串、疑似私钥格式的内容,可能是数据外泄的伪装形态;二是结构化结果约定,要求Agent生成的代码只通过约定的JSON结构返回结果,其余输出视为异常信号。
逃逸防护上最容易踩的坑有三个。其一,seccomp默认配置在旧版本Docker中保留了危险syscall,如ptrace,建议使用自定义seccomp profile并默认拒绝未知调用。其二,以root身份运行容器内进程会放大内核漏洞的利用面,应该在Dockerfile中创建普通用户。其三,宿主机的Docker socket绝不能挂进沙盒,等于把宿主机root权限送了出去。此外,沙盒宿主机本身要保持内核更新,并尽量部署在独立的节点或独立账号下,做到纵深防御,即便沙盒被突破,攻击者拿到的也只是一个空壳环境。
总结
Agent安全沙盒不是单一技术,而是一组分层措施的组合:用容器或Wasm划定边界,用cgroups与超时约束资源,用网络策略切断外传通道,用审计日志保留追责能力。选型上,高吞吐轻任务优先考虑Wasm,复杂依赖任务使用强化容器,中间场景用seccomp进程沙盒。无论选择哪条路线,记住一个基本原则:对模型生成的代码,默认一切不可信,所有能力必须显式授予。这套思路不仅适用于代码执行,同样可以推广到文件操作、Shell调用等一切Agent动态能力的安全设计上。