读书笔记 · 约 14,408 字 · 预计推送 2 部分

主题精读:Agent 工具权限与沙箱——最小权限、审批流、审计日志与隔离执行 未开始

目录

题记与说明


一、核心问题:这份工作在解决什么问题

LLM Agent 的本质矛盾:模型要干活就需要高权限工具(shell、文件系统、网络、邮件、支付),但模型本身既会犯错、又可能被提示注入劫持——注入者不需要攻破系统,只需要让模型"自愿"调用你已经授权给它的工具。这个主题把矛盾拆成四个可操作的子问题:

子问题一句话对应来源
给多少权限工具级/路径级/数据级的最小权限怎么切Claude Code 权限文档、WorkOS、CaMeL
什么时候停下问人审批流按什么标准触发,怎样才不算"安全表演"WorkOS、Permit.io、Claude Code 权限模式
事后如何追责什么样的日志才算"审计证据"而非"遥测"Permit.io、WorkOS
如何在模型之外兜底OS 级隔离如何做到"注入绕过模型决策也出不去"Claude Code 沙箱、Codex 沙箱分析

贯穿全线的根驱动是提示注入未解决。CaMeL 论文与 Willison 都把这一点作为出发点:

"we believe it is unlikely that general-purpose agents can provide meaningful and reliable safety guarantees."
(我们认为通用型 agent 不太可能提供有意义且可靠的安全保证。)
— Willison 引述的论文判断,*Design Patterns for Securing LLM Agents*
"any exposure to potentially malicious tokens entirely taints the output for that prompt"
(任何对潜在恶意 token 的暴露都会完全污染该 prompt 的输出——包括文本输出和它能发起的工具调用。)
— Simon Willison 的心智模型

既然模型层不可信,安全的锚点就必须下沉到模型之外的机械层:规则引擎、审批人、操作系统内核、审计账本。这就是四层结构的由来。


二、主要主张:四层防御的收敛

七个来源虽然立场不同(厂商文档、学术系统、逆向分析、治理指南),但收敛到同一个四层模型:声明式权限规则 → 人在环审批 → OS 沙箱 → 审计日志。每一层的存在理由都是"上一层可以被绕过"。

2.1 第一层:声明式权限规则(快,但官方自己承认不是安全边界)

Claude Code 的权限规则是三层求值的声明式系统,核心设计是 deny-first:

"Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn't change the order."
(规则按 deny → ask → allow 顺序求值,第一个命中的决定结果,规则的具体程度不改变求值顺序。)
"An allow rule can't carve an exception out of a deny rule."
(allow 规则不能从 deny 规则里挖出例外。)

三个值得记录的工程细节:

  1. 规则由 harness 而非模型执行:"Permission rules are enforced by Claude Code, not by the model."——prompt 和 CLAUDE.md 里的任何话都改变不了允许范围。这是所有可靠权限系统的共同前提:策略点(PDP)必须在模型之外。
  2. 裸工具名 deny 是从模型上下文里直接删除工具(如 Bash),模型根本"看不见"这个工具;而带参数的 deny(如 Bash(rm *)) 保留工具但拦截匹配调用。前者更彻底,因为连"尝试-失败-变通"的循环都不会发生。
  3. 字符串层规则的天然缺陷被官方文档自己承认:Bash(curl *) 这类 deny 挡不住 /usr/bin/curl 或 sh -c 'curl ...',文档明说这类模式 "are fragile",不是安全边界,真要兜底请上沙箱或 hooks。compound command(&&、| 等)要求每个子命令都被允许才能免提示,是对这个缺陷的部分修补。

设置层级也是最小权限的一部分:managed settings(企业管控)最高、任何层级包括命令行参数都不能覆盖——"no other level, including command line arguments, can override a managed permission rule";跨层冲突一律 deny 赢("if user settings allow a permission and project settings deny it, the deny rule blocks it")。bypassPermissions 模式的文档警告与主题直接相关:只该在"isolated environments like containers or VMs"里用。

2.2 第二层:审批流(人在环)——按可逆性分类,且必须 out-of-band

Claude Code 用权限模式(permission modes)把"问不问人"变成显式档位:default(首次使用每工具提示)、acceptEdits(自动接受文件编辑)、plan(只读)、auto(免常规提示但由后台分类器审查 shell/网络动作)、dontAsk(一律拒绝本应提示的调用)、bypassPermissions(跳过提示,仅限隔离环境)。"Yes, and don't ask again" 的保存粒度也有讲究:Bash/域名规则按仓库+命令/域名永久保存,文件修改类批准只在会话内有效——批准的半衰期与动作的可逆性挂钩。

WorkOS 给出审批分类的通用标准,这是七源中对审批流最系统的表述:

按三轴分类动作:reversibility(可逆性)、blast radius(爆炸半径)、sensitivity of data touched(触碰数据的敏感度)。低风险(读、草稿、查询)自动放行;高风险(发送、部署、转账、删除、授权)——"human approval in the loop is a primary control, not a courtesy"(人的批准是主控制,不是礼节)。

两条容易被忽视的硬约束:

"a confirmation prompt that the agent itself renders and answers provides no security at all."
(由 agent 自己渲染、自己应答的确认框没有任何安全价值——被劫持的模型连确认框一起伪造。)
二元的"完全自主 vs 事事都问"都不可取:前者爆炸半径失控,后者会退化成"security theater"——用户形成反射性点"允许"的习惯。

Permit.io 补上审批的证据属性:当策略要求人工批准时,"the approval is part of the authorization evidence, not a side conversation in Slack"——审批人、时间、关联请求必须进入审计账本并共享同一个 trace ID,否则审计时无法证明"策略要求批准 → 某人在某时批准 → 副作用随后执行"这条链。

2.3 第三层:OS 沙箱(机械兜底)——两个标杆实现的共同设计

这是本次精读信息密度最高的部分。Claude Code 与 Codex 的沙箱在互不知会的情况下收敛到几乎相同的设计:

"The Bash sandbox is a boundary that the operating system enforces around the shell commands Claude runs on your machine."
(Bash 沙箱是操作系统对 Claude 所运行 shell 命令强制施加的边界——由内核执行,不由模型遵守。)
"sandbox restrictions still apply even if a prompt injection bypasses Claude's decision-making."
(即使提示注入绕过了 Claude 的决策,沙箱限制依然生效。)

共同设计模式(两实现逐项对照):

设计点Claude CodeOpenAI Codex CLI
macOS 机制Seatbelt(系统内置,免安装)/usr/bin/sandbox-exec(路径硬编码),profile 以 (deny default) 开头
Linux 机制bubblewrap(文件系统隔离)+ seccomp(可选,封 Unix socket)+ socat 走代理bubblewrap(vendored,命名空间隔离)+ Landlock(LSM 文件访问控制)+ seccomp(网络限制)
Windows原生不支持,"runs commands unsandboxed",需 WSL2;failIfUnavailable 时直接退出受限进程 token(codex-windows-sandbox crate),默认关闭
网络控制"A sandboxed command has no direct route to the network." 流量强制过本地代理,按域名 allowlist 过滤;代理拒绝解析到本机地址的主机名(loopback、169.254.169.254)——封死云元数据端点窃凭证的通道启用代理时"network access is restricted to the proxy's loopback port only"(如 (allow network-outbound (remote ip "localhost:43128"))),MITM 代理(rama 框架)处理 HTTP/SOCKS
防自我提权(关键细节)protected paths:.claude 设置/skills/agents/hooks、.mcp.json、shell rc、.gitconfig、git internals(HEAD/objects/refs)即使在可写目录内也禁写。理由一句话:"A command that could edit those files could grant itself permissions, or add a hook or MCP server.".git/ 与 .codex/ 在可写根内自动标记为只读子路径,防止"Modifying git hooks (e.g., .git/hooks/pre-commit)"和"Changing sandbox configuration (.codex/config.toml)"
凭证保护sandbox.credentials:deny(文件不可读、环境变量清空)或 mask(命令看到的是会话级哨兵值,代理在放行出站请求时替换真值;AWS SigV4 由代理重签)(未见对应机制;有 .env 加载时过滤 CODEX_ 前缀变量)
失败逃逸通道沙箱失败可重试 dangerouslyDisableSandbox(会提示);strict 模式下直接忽略该参数——"Claude Code ignores the dangerouslyDisableSandbox parameter"is_likely_sandbox_denied() 检测命令是否因沙箱而失败;DangerFullAccess 完全无沙箱

Codex 的进程加固是 Claude Code 文档未展开的一层:ptrace(PT_DENY_ATTACH) 反调试、RLIMIT_CORE=0 禁 core dump、清除 DYLD_*/LD_*(防库注入/preload 注入)、release 版拒绝在系统临时目录下运行 CODEX_HOME。这些防的是"沙箱内进程攻击沙箱本身"。

一个清醒的边界:agent-safehouse 对 Codex 的分析开宗明义——

Codex "sandboxes its own tool calls, not itself — the main Codex process runs unsandboxed; only the shell commands it executes for the AI are sandboxed."
(Codex 沙箱化的是它自己的工具调用,不是它自己:主进程不沙箱,只有它替 AI 执行的 shell 命令被沙箱化。)

Claude Code 同样:"The sandbox covers shell commands only."——文件工具、WebFetch/WebSearch、hooks、MCP 服务器全部运行在沙箱之外,只受权限规则约束。两个工业标杆的沙箱都只罩住了最危险但最不可控的攻击面(任意 shell),而不是整个 agent。

2.4 第四层:审计日志——遥测不是证据

Permit.io 的核心区分是本主题对"审计日志"最有价值的贡献:

"Telemetry explains model behavior. AuthZ evidence explains control enforcement."
(遥测解释模型行为;授权证据解释控制是否被强制执行。)

LLM trace 记录模型说了什么,tool trace 记录什么跑了,但两者都证明不了"这个动作在当时的策略版本下、对那个 agent、代表那个用户、针对那个资源是否被允许"。最小证据包(authorization envelope)共九项:agent 身份、人类 principal 或委托链、工具与参数、资源与动作、策略 ID 与版本、决定(allow/deny)与理由、审批记录、时间戳/请求 ID/租户环境、副作用结果。

三条工程规则:

  1. 在执行点记录,不是事后补:"If you log only after the tool runs, you missed the control point."——授权日志应发生在 PDP 或 MCP 网关这个策略评估点,并与工具 trace、下游变更用共享 correlation ID 串起来。
  2. deny 日志反而是控制存活的证明:"Explainable deny logs prove the control is active."——只记录 allow 的审计在事后看来等于没有控制。
  3. Replay 是重建决策而非重跑模型:"does not mean re-running the model with a new temperature"——用同一 principal、agent、工具、资源、策略版本、属性、审批状态重放授权决策,供审计员核对。为此日志必须不可篡改(immutable, append-only)且受到保护——WorkOS 补一刀:"A rotated key that still lives in a forgotten log is not rotated"(日志里躺着旧密钥等于没轮换)。

2.5 最小权限的四个粒度:从"少给权限"到"数据级能力"

把七个来源的说法叠起来,最小权限在这个领域至少有四个递进粒度:

  1. 工具级:不需要的工具直接从模型上下文删除(裸 deny)。——Claude Code
  2. 路径/域名级:写范围 = 工作目录 + 临时目录 + 显式 add-dir;网络 = 空 allowlist 起步;读默认近乎全机但要 deny 掉 ~/.ssh、~/.aws/credentials。——Claude Code 沙箱
  3. 身份级:agent 是独立 principal,"should not authenticate by reusing a user's session cookie, OAuth token, or long-lived API key";用 token exchange(RFC 8693)让结果 token 同时携带 agent 与用户两个身份声明;agent 权限永远不超过 agent 权限与用户权限的交集(confused deputy 防御的核心句式:"never exceed the intersection of what the agent and the user are each permitted to do");凭证寿命"measured in minutes"。——WorkOS、Permit.io
  4. 数据级(能力标签):CaMeL 给每个值挂 provenance(来自用户/工具/转换)与 allowed readers(可见者集合),依赖图随数据流传播,每次工具调用前对完整依赖图校验策略。例如护照号标记为"只能被受信政府工具处理",而姓名/电话可以流向订票工具。这是最小权限从资源维度到数据维度的下沉,思想源自 libcap/Capsicum/CHERI。——CaMeL

2.6 根驱动的系统级解法:CaMeL 与六模式

CaMeL 的主张是:与其让模型"识别注入",不如让注入数据在结构上进不了控制流:

CaMeL "provides an environment where predefined policies prevent unintended consequences caused by prompt injection attacks."

双 LLM 结构:P-LLM(特权)只看用户查询、产出 Python 计划代码,"only interacts with the user query and not the data returned by tools"(工具输出进变量,P-LLM 永远看不到内容);Q-LLM(隔离)无工具权限,只把非结构化数据解析成 schema,且"the Q-LLM cannot communicate to the P-LLM what information it needs, as this could be a vector for prompt injections"(连"我还需要什么信息"都不能回传,因为那本身就是注入通道)。自定义 Python 解释器维护数据流依赖图,STRICT 模式让控制流块内的语句依赖其条件/迭代对象,封堵条件取图、异常侧信道等位泄漏。

Willison 把这类结构总结为六个约束 agent 循环的设计模式:Action-Selector(LLM 调制的 switch,永不看响应)、Plan-Then-Execute(接触不可信数据前先定计划)、LLM Map-Reduce(子 agent 只回窄输出如布尔值)、Dual LLM(Willison 2023 原始版)、Code-Then-Execute(CaMeL,"an improved version of my dual LLM pattern")、Context-Minimization。他给 CaMeL 的定位是行业里"第一个可信的、面向工具使用 LLM 系统的方案",并明确接受代价:"We don't have a magic solution to prompt injection, so we need to make trade-offs."(没有魔法解,就必须做取舍——限制 agent 干任意任务的能力。)


三、方法:各来源怎么论证

来源类型论证方法
Claude Code 权限/沙箱文档官方实现文档规则语义的精确定义(求值顺序、作用域、失败模式)+ 已知限制的主动披露("fragile"、"covers shell commands only")
agent-safehouse Codex 分析第三方逆向从开源代码(Rust SandboxPolicy 枚举、Seatbelt profile 文本、进程加固 syscall)逐层还原设计,给出弱点评估("None identified")
CaMeL学术系统论文设计(能力+信息流控制,借用经典系统安全)+ 在 AgentDojo 基准上与 undefended 系统和提示词基线(tool filter、spotlighting、prompt sandwiching)对照实验
Willison从业者分析从论文中提炼可复用的模式语言,附攻击者视角的怀疑("run_rm_dash_rf_for_compliance()" 式的命名滥用)
Permit.io / WorkOS治理指南从审计员/CISO 视角反推证据需求与控制点,无量化实验,是最佳实践综合

方法上的互补关系:官方文档告诉你怎么配,逆向分析告诉你配了之后内核里真实发生什么,论文告诉你理论上还能收紧到什么程度,治理指南告诉你怎么向审计员证明这一切有效。


四、数据与证据

CaMeL(唯一有量化基准的来源):

Claude Code(文档披露的语义事实):deny→ask→allow 求值顺序;deny 跨作用域必胜;managed 规则不可被任何层级覆盖;沙箱默认读范围包含 ~/.ssh 与 ~/.aws/credentials(所以必须显式 credentials deny——这是个反直觉的默认);代理拒绝 loopback 与 169.254.169.254;.git internals 与 .claude 属 protected paths;strict 模式忽略 dangerouslyDisableSandbox;原生 Windows 无沙箱。

Codex(逆向分析确认的代码事实):Seatbelt profile (deny default) 开头;.git/、.codex/ 自动只读子路径;Linux 三件套 bubblewrap+Landlock+seccomp;网络限制到代理 loopback 端口;env_clear()、kill_on_drop(true) 的子进程孵化;ptrace 反调试 + core dump 禁用 + DYLD/LD 清洗;该报告弱点评估为 "None identified"。

WorkOS / Permit.io(规范性主张,无实验数据):短时凭证、RFC 8693 委托流、九项证据包、enforcement-point 记录原则、out-of-band 审批原则。注意:这两篇是最佳实践推断,没有量化效果数据支撑(如"审批疲劳导致多少漏批"无实证)。

未验证项(明确标注):① Reddit 有帖称 Codex 存在两个沙箱逃逸漏洞可致宿主机命令执行——本次未验证原始披露,agent-safehouse 报告结论却是 "None identified",两者矛盾,以未定论处理;② Codex 的审批策略细节(untrusted/on-failure/on-request/never 档位)出现在检索摘要中,但 agent-safehouse 全文明确"approval policies not covered in this document",本次未能从一手来源核实,不作主张;③ Claude Code 的 auto 模式分类器细节有专门页面,本次未读。


五、结论

  1. 四层模型是行业收敛点,不是某家发明:声明式规则(快、可绕)→ 人在环审批(慢、覆盖不可逆动作)→ OS 沙箱(内核执行、注入绕过决策也出不去)→ 审计(事后证明控制有效)。每层单独都不充分:官方文档自己承认字符串规则 fragile,沙箱只罩 shell,审批会被反射性批准稀释,审计不落在执行点就是废纸。
  2. 最小权限正在从"资源粒度"下沉到"数据粒度":工业界(Claude Code/Codex)停在路径+域名+凭证粒度;学术前沿(CaMeL)已把最小权限做成 per-value 的能力标签与依赖图,代价是 ~2.8× token 开销与策略维护负担。
  3. 防自我提权是沙箱设计中最容易被忽视的一课:agent 能改自己的配置/hook/git hooks = 无条件提权。两个标杆都把"agent 可自我修改的面"设为最高保护(.git/.codex/.claude 只读),这个模式应推广到所有 agent 系统。
  4. 审计的对象是"授权决策"而非"执行过程":九项证据包 + 策略版本 + 不可篡改 + 执行点记录 + deny 日志,才构成"控制存在且生效"的可证明命题;LLM trace 和 tool trace 只是遥测。
  5. 提示注入决定天花板:在通用 agent 上不做取舍的安全方案目前不存在;可行路径是 Willison 六模式 + CaMeL 式系统结构 + 分层防御,明确牺牲一部分 agent 能力换取可证明边界。

六、局限

来源层面:

主题本身的局限(跨来源综合判断):


七、对我的启发

  1. 四层模型可直接套用于自己的 agent/工具权限设计:先 deny-by-default(裸工具名 deny 直接从上下文删除),再收路径与域名,高风险动作走审批,全程在策略评估点落审计。判断一个 agent 系统的安全性,先问四个问题:deny 是否赢?审批是否 out-of-band?有没有 OS 层兜底?deny 日志是否存在?
  2. "防自我提权"清单:任何 agent 系统都要把 agent 能修改自身的面(配置文件、hook 定义、MCP 注册、启动脚本、git hooks)列为最高保护级。这个检查项在通用安全清单里常缺位,但两个标杆都把它当核心设计。
  3. 审批设计的两条铁律:① agent 自己渲染的确认框不算审批;② 按可逆性×爆炸半径×数据敏感度三轴分档,避免"事事都问"退化为反射性放行。审批记录要进证据链(审批人/时间/trace ID),否则事后无法向任何人(包括未来的自己)证明控制有效。
  4. 审计日志的实现位置:在策略执行点(网关/PDP)记授权决策九元组,而不是事后从 tool trace 里"整理"审计——"If you log only after the tool runs, you missed the control point."这一条对自己的动态工作流系统(subagent、任务编排)同样成立。
  5. 写作素材:Claude Code vs Codex 的沙箱对照表(2.3 节)是现成的对比分析框架;".git 只读防 pre-commit 劫持"和"169.254.169.254 元数据端点封堵"是两个能讲清"为什么需要 OS 层"的具体故事;CaMeL 的"用 7% 效用换可证明安全 + 2.8× token"是"安全-能力权衡"的最佳量化案例。
  6. 读安全类系统设计的方法收获:官方文档给语义、逆向分析给事实、论文给上界、治理指南给证据需求——对同一主题取四种视角,才能区分"宣称的设计"与"实际执行的机制"。

附录:关键原文引述备查

#引述出处
1"Rules are evaluated in order: deny, then ask, then allow. …"Claude Code permissions
2"Permission rules are enforced by Claude Code, not by the model."Claude Code permissions
3"An allow rule can't carve an exception out of a deny rule."Claude Code permissions
4"…are fragile"(对 Bash 字符串匹配规则的自评)Claude Code permissions
5"sandbox restrictions still apply even if a prompt injection bypasses Claude's decision-making."Claude Code permissions
6"The Bash sandbox is a boundary that the operating system enforces…"Claude Code sandboxing
7"The sandbox covers shell commands only."Claude Code sandboxing
8"A sandboxed command has no direct route to the network."Claude Code sandboxing
9"A command that could edit those files could grant itself permissions, or add a hook or MCP server."Claude Code sandboxing(protected paths 理由)
10"Claude Code ignores the dangerouslyDisableSandbox parameter."(strict 模式)Claude Code sandboxing
11Codex "sandboxes its own tool calls, not itself…"agent-safehouse Codex
12".git/ and .codex/ directories within writable roots are automatically marked as read-only subpaths"agent-safehouse Codex
13"network access is restricted to the proxy's loopback port only"agent-safehouse Codex
14"None identified"(该报告对 Codex 弱点的结论)agent-safehouse Codex
15"provides an environment where predefined policies prevent unintended consequences…"CaMeL 论文
16"only interacts with the user query and not the data returned by tools"(P-LLM)CaMeL 论文
17"the Q-LLM cannot communicate to the P-LLM what information it needs…"CaMeL 论文
18"No, prompt injection attacks are not fully solved."CaMeL 论文(作者自答)
19"any exposure to potentially malicious tokens entirely taints the output for that prompt"Willison
20"We don't have a magic solution to prompt injection, so we need to make trade-offs."Willison
21"Telemetry explains model behavior. AuthZ evidence explains control enforcement."Permit.io
22"If you log only after the tool runs, you missed the control point."Permit.io
23"Explainable deny logs prove the control is active."Permit.io
24"the approval is part of the authorization evidence, not a side conversation in Slack."Permit.io
25"Agents should not authenticate by reusing a user's session cookie, OAuth token, or long-lived API key."WorkOS
26"never exceed the intersection of what the agent and the user are each permitted to do"WorkOS
27"Prefer credentials with lifetimes measured in minutes."WorkOS
28"a confirmation prompt that the agent itself renders and answers provides no security at all."WorkOS
29"human approval in the loop is a primary control, not a courtesy"WorkOS
30"A rotated key that still lives in a forgotten log is not rotated."WorkOS
← 资料库今日播报 →