安全沙箱设计
安全沙箱设计
学习目标
读完本章后,你将能够:
- 理解 AI Agent 安全沙箱的多层防御架构
- 掌握 Linux(bwrap/seccomp/landlock)和 macOS(Seatbelt)沙箱技术
- 设计命令级别的安全策略引擎
- 评估不同沙箱方案的适用场景和权衡
前置知识
- Linux 命名空间(namespace)基础
- 系统调用(syscall)基本概念
- 进程权限模型(UID/GID、capabilities)
核心概念
1. 为什么 Agent 需要沙箱
AI Agent 具有执行任意代码的能力,这既是其价值所在,也是最大的安全风险:
- 数据泄露:Agent 可能将敏感代码上传到外部服务
- 系统破坏:Agent 可能误删文件、修改系统配置
- 恶意利用:被诱导执行恶意命令(如删除数据库、加密勒索)
- 供应链攻击:Agent 下载并执行不可信依赖
沙箱的目标是:即使 Agent 被攻击或出错,其造成的损害也被限制在可控范围内。
2. 多层防御架构
生产级 Agent 沙箱采用**纵深防御(Defense in Depth)**策略:
每层都是独立的防线,即使一层被突破,其他层仍然提供保护。
3. Linux 沙箱技术栈
Bubblewrap(bwrap)— 文件系统隔离
bwrap 是一个轻量级的容器化工具,无需 root 权限即可创建隔离的文件系统视图:
bwrap \ --ro-bind / / \ # 根目录只读挂载 --bind /home/user/proj /home/user/proj \ # 特定目录可写 --tmpfs /tmp \ # 独立的 /tmp --unshare-net \ # 隔离网络命名空间 -- /bin/bash关键特性:
- 用户命名空间(user namespace):普通用户可运行
- 文件系统视图完全可控:只读、可写、不可见
- 设备树最小化:
--dev /dev只提供基本设备
seccomp — 系统调用过滤
seccomp(secure computing mode)是 Linux 内核的安全机制,可以限制进程可调用的系统调用:
白名单模式:只允许显式列出的 syscall黑名单模式:只允许未显式禁止的 syscall网络相关的 seccomp 策略:
- Restricted 模式:阻断
connect/accept/bind/listen等,仅允许AF_UNIXsocket - ProxyRouted 模式:仅允许
AF_INET/AF_INET6,强制流量走代理桥
危险 syscall 过滤:
ptrace:进程调试,可被用于提权process_vm_readv/writev:跨进程内存读写io_uring_*:新型异步 I/O,攻击面大
no_new_privs
PR_SET_NO_NEW_PRIVS 是一个进程标志,禁止该进程及其子进程通过 execve 获取更高权限(如 SUID 程序):
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);这是防止沙箱内执行提权程序的关键防线。
4. macOS Seatbelt
macOS 使用 sandbox-exec 工具和 SBPL(Sandbox Profile Language)定义沙箱策略:
sandbox-exec -p "(version 1)(allow default)(deny network*)(allow file-read* (subpath \"/home/user\"))" -- /bin/bash特点:
- 基于 Mach 内核的强制访问控制(MAC)框架
- 策略文件编译为二进制格式,内核直接执行
- 支持细粒度的文件系统、网络、进程权限控制
5. 命令策略引擎
除了系统级沙箱,Agent 还需要命令级别的策略控制:
策略模型:
PrefixRule:基于命令前缀的匹配规则(如["git", "status"])NetworkRule:基于目标主机和协议的规则Decision:Allow/Prompt/Forbidden
策略语言:使用 DSL(如 Starlark)表达策略,支持:
- 前缀匹配:
prefix_rule(pattern=["git", "status"], decision="allow") - 网络规则:
network_rule(host="api.github.com", protocol="https", decision="allow") - 运行时追加:用户批准后动态添加规则
设计模式详解
权限提升控制
沙箱内启动的交互式 shell 可能执行外部命令,需要控制其权限边界:
三种决策:
Run:在沙箱内直接执行(透明 execv)Escalate:提升到服务器侧执行,可指定沙箱级别Deny:拒绝执行
实现机制:
- 通过
EXEC_WRAPPER环境变量注入 execve wrapper - Wrapper 通过 Unix Domain Socket 向 EscalationServer 发送请求
- 每个请求使用独立的 stream socket,避免并发冲突
代理网络路由
当沙箱隔离网络命名空间时,需要为合法的网络请求提供出口:
实现:
- 在主机侧创建 Unix Domain Socket 桥接器
- 在 bwrap 隔离的网络命名空间内,将代理环境变量指向本地 UDS
- 代理桥将所有流量转发到配置的 HTTP/SOCKS5 代理
这实现了网络隔离 + 强制代理的组合:进程无法直接访问外部网络,所有流量必须经过审计过的代理。
问题与规避
bwrap 在容器环境中的兼容
问题:在 Docker 容器内运行 bwrap 时,--proc /proc 等选项可能被拒绝。
对策:
- 检测容器环境,自动降级沙箱策略
- 回退到纯 seccomp 过滤(不隔离文件系统)
- 提供
--dangerously-bypass-sandbox应急选项
seccomp 的误伤
问题:过于严格的 seccomp 规则可能阻断合法的系统调用(如某些动态链接库的初始化)。
对策:
- 基于实际使用场景测试 seccomp 规则
- 提供
--seccomp-log模式:只记录不阻断,用于调试 - 允许用户通过配置文件添加自定义允许列表
策略文件的并发修改
问题:多个 Agent 实例同时修改策略文件,可能导致数据损坏。
对策:
- 使用文件锁(flock)保护策略文件的读写
- 原子写入:先写入临时文件,再重命名替换
- 策略文件使用追加模式,减少写冲突
设计取舍
强隔离(内核沙箱)vs 轻量控制(策略引擎)
| 维度 | 内核沙箱(bwrap/seccomp) | 策略引擎(DSL) |
|---|---|---|
| 隔离强度 | 强(内核强制执行) | 弱(应用层检查) |
| 性能开销 | 中(命名空间切换) | 低(纯软件检查) |
| 灵活性 | 低(需要 root/特定权限) | 高(动态规则) |
| 用户体验 | 透明(用户无感知) | 显式(提示审批) |
推荐策略:内核沙箱作为底线保障,策略引擎作为精细化控制。两者结合实现纵深防御。
自动化审批 vs 人工确认
| 方案 | 优势 | 代价 |
|---|---|---|
| 全自动 | 流畅、无中断 | 安全风险、难以审计 |
| 全人工 | 绝对安全、可审计 | 体验极差、效率低 |
| 规则驱动 | 平衡安全与流畅 | 策略配置复杂 |
| 学习模式 | 自动适应用户习惯 | 冷启动问题、错误学习 |
推荐策略:初始阶段采用全人工确认,随着规则积累逐步过渡到规则驱动。
浏览器环境中的域名级沙箱
在浏览器自动化场景中,Agent 会访问不可信的第三方网页。此时沙箱的边界不是进程,而是域名:
域名白名单/黑名单机制:
browser = BrowserSession( allowed_domains=["*.google.com", "github.com"], prohibited_domains=["malicious-site.com"],)域级凭据注入:
agent = Agent( sensitive_data={ "github.com": {"username": "user", "password": "pass"}, "*.google.com": {"email": "user@gmail.com"}, },)凭据仅在与 allowed_domains 匹配的域上自动注入。当检测到 sensitive_data 但未设置 allowed_domains 时,系统应发出安全警告——因为 Agent 访问恶意网站时可能遭遇 prompt-injection 攻击,导致凭据被窃取。
域模式匹配:
- 精确匹配:
"github.com"匹配github.com - 通配符匹配:
"*.google.com"匹配mail.google.com、docs.google.com - 协议无关:匹配时忽略
http:///https://前缀
弹窗自动关闭作为 Prompt-Injection 防御
网页可以通过 JavaScript alert()、confirm()、prompt() 弹出对话框。在浏览器自动化场景中,这些弹窗可能被恶意网页用于:
- 模拟系统登录界面:诱导 Agent 输入凭据
- 显示伪造的”操作成功”消息:欺骗 Agent 认为任务已完成
prompt()窃取信息:弹出的输入框可能诱导 Agent 输出敏感数据
防御策略:PopupsWatchdog 监听 JavaScript 对话框事件,自动关闭并记录消息内容。被自动关闭的弹窗消息会注入到 Agent 的上下文中(如 Auto-closed JavaScript dialog: "Enter your API key"),让 Agent 知道发生了什么。
设计权衡:
- 优点:防止所有基于弹窗的 prompt-injection 攻击
- 代价:合法的确认弹窗也会被自动关闭(如”确定要删除吗?”)
- 替代方案:将弹窗转发给 Agent 决策(增加复杂度但更灵活)
参考来源
补充:Cline 的审批模式与安全控制
来源:Cline(cline/cline)apps/vscode/src/core/task/tools/autoApprove.ts、apps/vscode/src/core/permissions/CommandPermissionController.ts,commit 791d238
三级自动审批策略
Cline 在应用层实现了三级自动审批,与内核沙箱形成互补:
各级别配置:
| 级别 | 配置项 | 安全等级 |
|---|---|---|
| Yolo Mode | mode: "yolo" | 最低,所有工具无确认执行 |
| Auto-approve All | mode: "autoApproval" | 中等,浏览器/MCP 工具仍需确认 |
| 细粒度 | per-tool alwaysAllow + 路径限制 | 最高,每个工具可独立配置 |
设计启示:审批策略应该是一个安全滑块,而非二元选择。从 Yolo(最快)到细粒度(最安全),用户可以根据当前项目敏感度动态调整。
.clineignore 文件访问控制
Cline 实现了一个类似 .gitignore 的 .clineignore 机制,限制代理访问特定文件和目录:
- 在所有文件列表、搜索、读取操作中自动过滤匹配的文件
- 支持 glob 模式(
node_modules/**、*.env、.git/**) - 即使工具被批准执行,也无法访问被
.clineignore屏蔽的文件
为什么需要两层文件控制? 审批控制的是”是否允许操作”,.clineignore 控制的是”操作范围”。即使 Agent 被批准写入文件,它也不应该访问 .git/ 或 node_modules/。
潜在陷阱:.clineignore 只在应用层过滤,不能替代文件系统级权限。如果 Agent 通过 execute_command 调用外部工具(如 find),可能绕过 .clineignore 的过滤。因此需要与 CommandPermissionController 联合使用。
链式命令分段验证
CommandPermissionController 对复杂命令的安全处理策略:
# 伪代码:链式命令验证function validateCommand(cmd: string): Decision { // 1. 按 && ; | 分段 const segments = splitByOperators(cmd)
// 2. 每段独立验证 for (const seg of segments) { if (!matchesAllowedPattern(seg)) { return Decision.Prompt // 需要用户确认 } }
// 3. 子 shell 递归验证 const subshell = extractSubshell(cmd) // $(...) or `...` if (subshell) { return validateCommand(subshell) // 递归验证 }
return Decision.Allow}重定向操作符检测:>、>>、< 等重定向可能用于覆盖敏感文件(如 ~/.ssh/authorized_keys)。Cline 对包含重定向的命令额外检查目标路径,如果被保护则拒绝执行。
设计取舍:完全禁止复杂命令会严重影响开发体验(如 ls | grep foo | head -5),但不验证又容易受到命令注入攻击。Cline 选择分段验证 + 子 shell 递归的中间方案:允许复杂命令,但每段都必须通过模式匹配。
GateGuard 事实强制模式
GateGuard 是一种在首次编辑/写入前强制 Agent 了解上下文的安全门控模式。
工作原理
核心规则:每个文件的首次编辑/写入操作被阻止,除非 Agent 能证明自己已经阅读并理解了该文件的上下文。后续编辑不再触发 Gate。
防御目标
| 攻击向量 | GateGuard 防御 |
|---|---|
| Agent 盲目修改不了解的文件 | 强制先 Read 再 Edit |
| 破坏隐含约束(如 schema 兼容性) | 强制检查导入者和数据消费者 |
| 绕过用户指令直接操作 | 强制对照用户指令执行 |
与审批模式的关系
GateGuard 不是传统的”是否允许操作”审批,而是操作顺序的强制约束:
| 维度 | 传统审批 | GateGuard |
|---|---|---|
| 控制点 | ”能不能做" | "做之前有没有先了解” |
| 触发条件 | 敏感工具调用 | 每个文件的首次编辑 |
| 解除条件 | 用户确认 | Agent 证明已阅读上下文 |
| 后续操作 | 每次都需要审批 | 首次通过后不再触发 |
设计优势
- 减少破坏性编辑:强制 Agent 在修改前先了解文件的完整上下文
- 防止”走捷径”:阻止 Agent 通过弱化 lint 配置来绕过错误(配合 Config Protection 钩子)
- 对每个文件只触发一次:不会在每个编辑轮次都增加延迟
陷阱
- 首次编辑延迟增加:Agent 需要额外一轮 Read 操作来了解上下文
- 对小型文件的过度防御:对于一目了然的小文件,GateGuard 可能增加不必要的步骤
- 缓解方案:GateGuard 可以通过
ECC_GATEGUARD=off临时关闭
补充:Hermes Agent 的信任模型分层
来源:Hermes Agent(NousResearch/hermes-agent)SECURITY.md、tools/approval.py,commit 2517917
唯一安全边界:OS 级隔离
Hermes 的安全策略有一个核心原则:进程内没有任何组件构成对对抗性 LLM 的 containment。审批门、输出脱敏、模式扫描器、工具白名单都是启发式组件,不是安全边界。
两层 OS 级隔离:
| 隔离类型 | 范围 | 适用场景 |
|---|---|---|
| 终端后端隔离 | 仅覆盖 shell/文件操作 | 可信 LLM + 防止误操作 |
| 全进程包装 | 整个 Agent 进程树 | 不可信输入源 + 生产部署 |
全进程包装支持 Docker Compose 和 NVIDIA OpenShell,后者提供声明式策略(文件系统、网络 L7 出口、进程/syscall、推理路由)。
审批门作为事故预防
Hermes 的审批门(tools/approval.py)有五层检测:
- Hardline:无条件阻止(
rm -rf /、mkfs、fork bomb 等),yolo 模式下也不允许 - Sudo stdin guard:阻止密码猜测攻击
- DANGEROUS_PATTERNS:约 47 个模式匹配(递归删除、chmod 777、SQL DROP 等)
- Smart Approval:使用辅助 LLM 评估风险
- Tirith:外部安全扫描
关键设计:审批门定位是合作模式下的错误捕捉,不是对抗性防护。Shell 是图灵完备的,字符串黑名单在结构上是不完整的。
凭证作用域
Hermes 默认从传递给子进程的环境中剥离凭证(API key、网关 token):
- Shell 子进程、MCP 子进程、代码执行子进程默认不继承凭证
- 技能/插件运行在 Agent 进程内,可以读取 Agent 能读到的所有内容
- 缓解措施:安装前审查,不是运行时脱敏
案例补充:CodeWhale 多平台沙箱与审批策略引擎
本补充基于 CodeWhale 源码分析,首次覆盖于 2026-06-01。
CodeWhale 展示了沙箱与审批策略分离设计的实践:
三层操作模式:
| 模式 | 写入权限 | 审批门 | 沙箱 |
|---|---|---|---|
| Plan | 只读 | 不适用 | 只读策略 |
| Agent | 可写入 | 每个破坏性操作需确认 | 平台沙箱(如有) |
| YOLO | 可写入 | 自动批准所有工具 | 平台沙箱(如有) |
多平台沙箱实现:
- macOS:Seatbelt(sandbox-exec)——活跃的强制访问控制策略
- Linux:Landlock(内核 5.13+)文件系统访问控制 + seccomp + bwrap(Bubblewrap)检测,但尚未正式执行
- Windows:计划通过 Job Object 进程树限制,目前不声称文件系统/网络/注册表隔离
- 进程加固(Linux v0.8.46+):
PR_SET_DUMPABLE=0、NO_NEW_PRIVS、RLIMIT_CORE=0
审批引擎关键设计:
- 审批与沙箱分离:
execpolicy负责审批决策,sandbox负责资源限制——两个正交的防御层 - 审批缓存:用户勾选”remember”后,同类操作后续自动批准,立即翻转当前 turn 的审批标志
- MCP 工具同流:MCP 发现的工具走同样的审批框架——只读 MCP 助手在 suggestive 模式下可跳过审批
- 五层审批检测(Hermes 模式参考):Hardline 无条件阻止 → Sudo 防御 → 模式匹配 → 辅助 LLM 评估 → 外部扫描
设计权衡:
- 审批门定位为”合作模式下的错误捕捉”,不是对抗性防护——Shell 是图灵完备的,字符串黑名单结构上不完备
- macOS Seatbelt 是唯一活跃的完整沙箱;Linux/Windows 沙箱在 helper 执行前不视为完整 OS 级隔离