沙箱与安全执行

U5
实战手册 · 编码与计算机操作智能体

编码智能体运行的是它刚针对一个刚读完的 issue 写出的不可信代码——沙箱是它与你基础设施之间唯一的东西。

编码智能体的标志性能力——执行代码以验证它——也是它标志性的风险。代码由模型生成,常被第三方 issue 或网页引导,而智能体会用它够得着的一切去运行它。本文涵盖容器化执行、文件系统与网络隔离、能力作用域,以及如何思考你没写也没评审过的代码的"爆炸半径"。

STEP 1

威胁模型:代码不可信,受攻击者影响。

两个相互叠加的事实。其一,模型生成的代码默认不可信——不是意图恶意,而是未经审计、可能意外 rm -rf、外泄或 fork 炸弹。其二,智能体的指令常来自攻击者可控的输入——一个 GitHub issue、一个被抓取的页面、某依赖的 README——使提示注入成为通往代码执行的路径,而不只是糟糕文本。沙箱必须假设代码主动想出去,因为有时上游确实有东西想出去。

STEP 2

隔离是分层的:容器,然后网络,然后文件系统,然后身份。

没有单一边界是充分的。容器(或用 microVM/gVisor 获得更强的内核边界)框住进程;显式的出站策略框住网络;一个有作用域、临时的文件系统框住代码能读或能毁的东西;而——团队最易遗忘的那个——没有环境凭据框住一次逃逸究竟值不值。一个能够到宿主云元数据端点、磁盘上还有凭据的容器不是沙箱;它是个跳板。

# the execution boundary, declared not assumed
sbx = Sandbox(
    net   = "deny",            # default-deny egress, allowlist only
    fs    = "ephemeral:/work",  # scoped, wiped per task
    creds = None,             # no ambient cloud/git identity
    limits= dict(cpu=2, mem="4g", pids=512, wall="10m"))
sbx.run(agent_generated_cmd)   # blast radius == this box, this task

现实中最常见的逃逸不是内核漏洞利用——而是一个够得着的云元数据端点、一个挂载的宿主套接字(/var/run/docker.sock 等于游戏结束),或 PATH 上一个长寿命 token。审计什么是够得着的,而不只是什么在"里面"。

STEP 3

网络是外泄通道;默认拒绝出站没有商量余地。

能上网的智能体能泄露它读得到的一切——源码、密钥、被窃 issue 的内容——还能拉取提示注入索要的第二阶段载荷。入站隔离是容易的那一半;出站才是要紧的那一半,也是多数搭建为了"就 pip install 一下"而留开的那一半。有纪律的姿态是默认拒绝加一份窄白名单(包镜像、git 远端),并对每个被允许的连接记日志,使一个装依赖的需求不会变成一扇敞开的门。

STEP 4

能力作用域:按任务授予、最小权限、限时。

静态、宽泛的权限是大爆炸半径的根因。把每项能力都收敛到任务上:一个只能读这个仓库别无其他的 token、只对工作树有写权限、一个把 fork 炸弹或挖矿程序压成噪声的墙钟与资源上限。问题从来不是抽象地"这个动作安全吗";而是"如果代码怀有敌意,这套凭据能干出的最坏是什么",而答案应当在构造上就很小且可恢复。

STEP 5

爆炸半径是设计指标,而非"它逃出去了吗"。

假设边界终将被跨越,并把设计做成跨越它也容易吸收:环境按任务临时化、用完即毁(没有植入物可赖以存活的持久层)、任务间无共享状态(一次被投毒的运行污染不了下一次)、从沙箱到生产或到另一客户数据没有路径。逃进一个没有值钱东西可偷、没有可横移之处的盒子,那是一份事件报告,不是一次入侵。再配上执行日志,使你事后能回答"它究竟干了什么"。

STEP 6

诚实的权衡。

更强的隔离要付出延迟、工程量与能力代价:真正气隙的沙箱跑不了需要真实数据库的集成测试,每任务一个 microVM 比一个线程慢。重点不是处处最大化隔离——而是隔离要与代码能触及的东西相匹配。绝不要用你不会交给陌生人的凭据或可达性去运行智能体生成的代码;唯一安全的假设是代码怀有敌意、写出它的那个 issue 也一样——为爆炸半径做工程,而不是寄望它守规矩。