如果我想写一个 code-driven 的浏览器 agent(类似于 ego-lite),那我该如何处理这个沙箱问题呢,有什么比较成熟的方案吗,如何保证它代码执行的一个安全性和异常回滚之类的
回复 群友 7309 这个问题核心是三个:隔离边界选在哪、执行权限怎么收、失败怎么回滚。分开说。 一、隔离边界:容器不是唯一答案,但要分层 最成熟的思路是把“代码执行”和“浏览器控制”放到不同的信任域: 代码执行放在沙箱里,浏览器跑在沙箱外面(宿主机或独立容器)。agent 写的脚本不能直接开浏览器,只能通过一层受控的 RPC/HTTP 接口发送“打开这个 URL”“点这个选择器”这类指令。这样即使脚本被 prompt injection 骗了,它能做的也只是发指令,而指令层可以校验、限频、记日志。 沙箱本身的选择,按成本从低到高: 一是进程级限制,用 seccomp-bpf、Landlock、namespaces(Linux 的 bubblewrap、nsjail 就干这事),成本低但需要自己拼装。 二是容器加 gVisor 或 Kata Containers,内核攻击面小很多,冷启动比普通容器慢一点,但比 VM 快,是现在多数托管代码执行服务在用的组合。 三是 microVM,Firecracker、Cloud Hypervisor,AWS Lambda 那套,隔离最强,冷启动百毫秒到秒级,适合多租户。 四是 WebAssembly 沙箱,Pyodide / wasmtime,启动最快、无系统调用风险,但生态受限,跑不了任意 Python 包,只能做纯计算或受限的解释执行。 真实产品里常见做法是:先用 wasm 挡掉大部分不可信小脚本,需要完整环境的走 microVM/容器池预热,不要每次冷启动。 二、权限收敛:四个默认拒绝 文件系统:默认只读,只挂一个固定的 workspace 目录可写,其他路径全挂 /dev/null 或直接不给。写出来的文件要限制配额。 网络:默认关闭出网。需要网络时走白名单代理,而不是给宿主机网络;这样 SSRF、内网探测、数据外传三条路都堵住了。这条是最容易被忽略、也最容易出事的。 系统调用:seccomp 白名单,只放开 execve、read/write、mmap 这些必需的,放弃 fork、ptrace、mount。 资源:CPU、内存、磁盘、执行时长、进程数都要有上限;超时是必须项,不是加分项。 另外别忘了密钥。沙箱里绝对不能有长期凭证,最多给一个短时效、窄 scope 的 token,用一次就废。 三、异常处理和回滚:靠 checkpoints,不靠 undo 代码执行这块没法像数据库那样真“回滚”,只能做状态快照和重放: 执行前给沙箱打快照(文件系统层、内存可选)。出错就丢弃这个实例重建,而不是在同一个实例上修。容器化之后,“重建”本来就比“清理”便宜,用不着撤销操作。 对于有副作用的外部动作(发邮件、提交表单、下单),这才是回滚的真正难点。成熟做法是把这类动作做成两阶段:先写意图到日志(intent log),执行后记结果;失败了走 compensating action(补偿操作)去撤,而不是指望撤销执行本身。动作必须幂等,带幂等键。 浏览器侧的状态回滚相对简单:每个任务一个独立的 browser context / profile,跑完销毁。脏了就是新 context 起,别去清理 cookie。 四、几件实践上容易踩的坑 超时和资源泄漏要覆盖三层:脚本本身、浏览器进程、CDP 连接。任何一层没死,沙箱就废了。 输出不设大小上限会被一个 print 循环炸掉内存,stdout 要截断。 要防“沙箱逃逸之外”的路径:脚本别拿到宿主机的 docker socket、别挂 /proc、别开特权模式,这些比内核漏洞常见得多。 可观测性:每一步指令、每次执行、每个退出码都留日志,出了事要能回溯是 agent 写错了脚本,还是站点变了。 如果你的规模不大,最低可行版本是:bubblewrap 或 Docker + seccomp + 无网络 + 只读根文件系统 + 时间超限 + 任务级独立 profile,这套组合一两天就能搭起来,性价比最高。等到跑多租户了再上 gVisor/microVM。