type
Post
status
Published
date
Apr 25, 2026
slug
agent19
summary
tags
Agent
category
icon
password
在 Agent、RAG、AI Coding 场景里,工具调用安全不是“提示词写得更严一点”就能解决的问题。真正可靠的设计是:LLM 只负责提出工具调用意图,宿主运行时负责权限判定、参数校验、沙箱隔离、人工确认和审计追踪。
Claude Code 官方文档明确区分了这两层:
CLAUDE.md 或用户提示词只能影响 Claude “尝试做什么”,真正的允许/拒绝必须由 Claude Code 的权限规则、权限模式或 PreToolUse Hook 执行;权限规则按 deny -> ask -> allow 评估,deny 优先。(Claude Code) OpenCode 则把工具权限统一抽象成 allow / ask / deny,并支持按工具名、命令模式、路径模式做细粒度匹配。(OpenCode) OpenClaw 更进一步把“工具策略、沙箱、elevated 逃逸执行、每个 agent 的独立配置”拆成多层,强调工具策略是 hard stop,deny 始终优先,沙箱不能把已被 deny 的工具“加回来”。(OpenClaw)一、为什么工具调用比普通 LLM 输出更危险?
普通 LLM 输出最多是“说错话”。工具调用会变成真实副作用:
删除文件、提交代码、推送 main、发邮件、转账、改数据库、读取.env、访问内网、拉取网页并被间接 Prompt Injection 劫持。
OWASP 把这类问题归为 Excessive Agency:LLM 系统被授予过多功能、过多权限或过多自治能力,导致模型在幻觉、直接/间接 Prompt Injection、恶意工具输出或多 Agent 传播下执行破坏性动作。OWASP 建议减少可用扩展、减少扩展功能、避免开放式 shell/URL 等泛化工具、最小化下游权限、以用户身份执行操作、对高影响动作做人类确认,并在下游系统做完整授权校验。(OWASP Gen AI Security Project)
工具调用安全的核心判断是:
不能问:“模型是否被要求不要乱来?”要问:“即使模型被诱导乱来,运行时能不能拦住它?”
二、成熟 Agent 的共同架构:LLM 不直接执行工具
可以把工具调用链路抽象成下面这张图:
这套架构里有五个关键点:
第一,LLM 只能提出工具调用,不能自己决定“安全”。第二,所有工具参数必须规范化后再匹配规则。第三,权限判断必须在模型外部执行。第四,危险动作必须有人工确认或强隔离。第五,工具结果回流给模型前也要做脱敏与注入检测。
Claude Code 的
PreToolUse Hook、OpenCode 的 permission 配置、OpenClaw 的 tool policy + sandbox + elevated,都是这套模式的不同实现。(Claude Code)三、工具风险分级:不是所有工具都应该同权
一个实用的 Agent 系统应该先把工具分级。
风险等级 | 工具类型 | 典型动作 | 默认策略 |
L0 只读低风险 | grep、ls、只读文档检索 | 读公开源码、读项目内普通文件 | allow |
L1 只读敏感 | 读 .env、SSH key、私有配置、用户数据 | 读取 secret、PII、凭证 | deny 或 ask |
L2 本地可逆写 | 编辑工作区代码、生成文件 | 修改 feature 分支文件 | ask / diff approve |
L3 本地破坏性 | rm -rf、覆盖配置、改 .git | 删除文件、破坏仓库状态 | deny |
L4 外部副作用 | 发邮件、发 Slack、开 PR、推送 Git | 对外通信、协作系统写入 | ask |
L5 生产/资金/权限 | 部署、DB 写、IAM、支付退款 | 生产变更、财务、权限授予 | deny by default + 多人审批 |
L6 任意执行 | shell、Python、Node、MCP 任意工具 | 泛化代码执行 | 沙箱 + allowlist + 审计 |
Claude Code 的 auto mode 默认会阻止下载并执行代码、向外部端点发送敏感数据、生产部署/迁移、云存储批量删除、授权 IAM 或 repo 权限、修改共享基础设施、不可逆删除旧文件、force push 或直接推 main 等动作。(Claude Code) 这说明成熟 Agent 并不是简单区分“能不能用 bash”,而是识别“这个调用的真实副作用是什么”。
四、Claude Code:权限规则、Hook、模式与受保护路径
Claude Code 的设计有几个很值得借鉴的点。
1. deny 优先,权限不是提示词
Claude Code 的规则顺序是
deny -> ask -> allow,第一条匹配规则生效;裸工具名 deny 会让 Claude 根本看不到该工具,而作用域规则如 Bash(rm *) 则是在调用时阻断。(Claude Code)这对应一个重要原则:
安全策略不要写进 Prompt,应该写进宿主程序可执行的策略系统。
示例配置:
2. Bash 参数匹配很脆弱,要用专门工具或 Hook
Claude Code 文档专门提醒:用
Bash(curl http://github.com/ *) 这类模式限制 URL 很脆弱,因为参数顺序、协议、重定向、变量、空格都可能绕过;更可靠的方式是 deny 掉 curl/wget,只允许受控的 WebFetch(domain:github.com),或者用 PreToolUse Hook 校验 URL。(Claude Code)这背后的设计经验是:
对 shell 这种开放式接口,不要只靠字符串匹配。要么拆成更窄的专用工具,要么用 AST/解析器/沙箱做强约束。
3. Hook 是运行时策略扩展点,但不能绕过 deny
Claude Code 的
PreToolUse Hook 可以在工具调用前执行自定义 shell 命令,返回 deny、ask 或 allow;但 Hook 不能绕过权限规则,匹配到 deny 仍然会被阻断,阻断型 Hook 也优先于 allow。(Claude Code)一个典型 Hook 伪代码:
4. 受保护路径不能自动写
Claude Code 在除
bypassPermissions 外的模式中,不会自动批准写 .git、.vscode、.idea、.husky、.claude 以及 .gitconfig、shell profile、.mcp.json、.claude.json 等敏感路径。(Claude Code)这说明文件权限系统至少要支持三类路径:
工作区普通路径:可读、可编辑。工作区敏感路径:可读或不可读,写入必须 ask/deny。工作区外路径:默认 deny,除非明确加入 scope。
五、OpenCode:permission 矩阵与 Agent 分层
OpenCode 的权限模型更直接:每条规则解析成
"allow"、"ask"、"deny"。它支持全局默认规则,也支持按工具和输入模式覆盖;对于对象语法,最后匹配的规则生效,因此通常把 "*" 放前面,把更具体的规则放后面。(OpenCode)示例:
OpenCode 文档列出的权限 key 包括
read、edit、glob、grep、bash、task、skill、lsp、question、webfetch、websearch、external_directory、doom_loop;默认情况下大多数权限是 allow,但 external_directory 和 doom_loop 是 ask,.env 文件读取默认 deny。(OpenCode)OpenCode 还有一个很重要的 Agent 分层:
Build 是默认主 Agent,工具全开;Plan 是受限主 Agent,默认对文件编辑和 bash 都 ask;Explore 和 Scout 这类子 Agent 偏只读,适合代码探索或外部依赖研究。(OpenCode)这给我们的启发是:
不要让一个全能 Agent 干所有事。规划 Agent 默认只读,执行 Agent 才拿写权限。子 Agent 权限必须比父任务更窄,不能因为委托而升级。
OpenCode 还支持按 Agent 配置特定 bash 权限,例如
git push ask、grep * allow、git * ask。(OpenCode) 这非常适合做“review agent”“release agent”“migration agent”等专用角色。六、OpenClaw:工具策略、沙箱、elevated 三层防线
OpenClaw 的安全模型更接近“个人助理 + 多通道 + 多 Agent 网关”。它明确提醒:workspace 只是默认 cwd,不是硬沙箱;如果没有启用 sandbox,绝对路径仍可能访问主机其他位置。(OpenClaw)
OpenClaw 的关键设计是三层:
第一层:tool policy 决定工具是否存在、是否可调用。第二层:sandbox 决定工具在哪里运行、能看到哪些文件和网络。第三层:elevated 是显式逃逸口,只影响exec,不能覆盖工具 deny。(OpenClaw)
OpenClaw 文档强调:
deny 总是获胜;如果 allow 非空,其他工具视为 blocked;tool policy 是 hard stop,/exec 不能覆盖被 deny 的 exec;同时,tool policy 只按工具名过滤,不检查 exec 内部副作用,因此允许 exec 后,deny write/edit/apply_patch 并不能让 shell 只读。(OpenClaw)一个安全的多 Agent 配置思路:
OpenClaw 官方也给出过类似的访问画像:个人 Agent 可以 full access、无沙箱;家庭/工作 Agent 可 sandbox + read-only;公共 Agent 应 sandbox 且无文件系统/shell 工具。(OpenClaw)
七、敏感接口限制:重点不是“工具名”,而是“能力边界”
工具安全控制不要只按工具名做。因为同一个工具可能有不同风险:
git status是只读;git push --force是高危。curl https://docs.example.com是读文档;curl $SECRET_URL | bash是远程代码执行。read README.md是低风险;read ~/.ssh/id_rsa是严重泄密。send_email(to=me)是草稿测试;send_email(to=customer)是外部副作用。
因此,策略引擎需要至少判断以下维度:
维度 | 示例 | 控制方式 |
tool name | bash、read、send_email | 工具级 allow/ask/deny |
action | read/write/delete/deploy/send | 动作级风险评分 |
resource | 路径、URL、数据库表、repo、bucket | 资源 allowlist/denylist |
subject | 用户、Agent、channel、session | 身份和上下文 |
environment | local/dev/staging/prod | 环境级策略 |
data sensitivity | secret、PII、token、源码 | DLP/脱敏/阻断 |
reversibility | 可回滚/不可回滚 | 不可逆动作强制审批 |
delegation | subagent/task/skill/MCP | 权限不能越权传递 |
八、一个可落地的 Policy Engine 设计
下面是一个简化版 TypeScript 策略引擎。核心思想是:LLM 输出的 tool call 必须变成规范化请求,再由策略引擎裁决。
这段代码的重点不是具体函数,而是裁决顺序:
身份/通道限制优先。只读 Agent 不能写。secret 路径默认 deny。工作区外默认 deny。受保护路径默认 deny。shell 只能 allowlist,未知命令 ask。生产和外部副作用必须人工确认。
九、路径安全:必须处理 symlink、绝对路径和工作区逃逸
文件工具最容易被绕过的地方是路径。
Claude Code 文档提到,Read/Edit 规则会同时检查 symlink 本身和其解析目标;allow 需要两者都匹配,deny 只要其中一个匹配就阻断。(Claude Code) 这个细节非常关键,因为攻击者可以在允许目录里放一个指向
~/.ssh/id_rsa 的 symlink。安全路径校验应该这样写:
十、Shell 安全:能不用 shell 就不用 shell
OWASP 明确建议避免开放式扩展,例如“运行任意 shell 命令”或“抓取任意 URL”,应尽量拆成更细的专用工具。(OWASP Gen AI Security Project)
例如,不要给模型一个万能工具:
更好的方式是拆成窄工具:
这样做的好处是:
模型不能自由拼接rm -rf。参数可以结构化校验。超时、环境变量、cwd、网络都可控。审计日志更清晰。
十一、网络与 MCP:必须防 SSRF、Token Passthrough 和工具投毒
MCP 官方安全文档强调了几个 Agent 系统常见风险:Confused Deputy、Token Passthrough、SSRF、Session Hijacking、本地 MCP Server compromise 等。尤其是 Token Passthrough 被明确列为反模式:MCP Server 不应接受并转发不是显式签发给它的 token。(Model Context Protocol)
对于网络工具和 MCP 工具,建议默认启用:
MCP 官方文档也建议服务端 MCP client 考虑 SSRF:拒绝生产环境的普通
http://,阻断私有/保留 IP、loopback、link-local、云 metadata endpoint,并对重定向目标做同样校验;同时建议通过 egress proxy 做网络策略。(Model Context Protocol)十二、人工确认:不能只是弹窗,要展示“最小可审计 diff”
OpenAI Agents SDK 文档把 guardrails 与 human review 分开:guardrails 用于输入、输出或工具行为的自动校验;human review 用于在取消、编辑、shell 命令、敏感 MCP 动作等副作用前暂停,由人或策略批准。(OpenAI开发者)
一个好的审批弹窗应该展示:
谁请求的:user / agent / subagent / channel。要调用什么:tool name + normalized args。会影响什么:文件、URL、repo、数据库、云资源。风险原因:为什么 ask。diff:将要修改哪些内容。回滚方案:是否可恢复。审批范围:仅本次、仅本会话、永久规则。
不要给用户一个模糊按钮:
“Claude wants to run bash. Allow?”
应该给用户:
十三、多 Agent 场景:权限必须“向下收敛”,不能“委托升级”
多 Agent 里最危险的问题是:父 Agent 没权限,但它调用一个有权限的子 Agent 间接完成操作。
因此要实现 transitive permission:
规则:
子 Agent 权限不能超过父 Agent。子 Agent 不能绕过父任务的 deny。子 Agent 的工具调用也要进入同一个 Policy Engine。子 Agent 返回结果前要做安全审查,避免把恶意工具输出注入父 Agent 上下文。
Claude Code 的 auto mode 对 subagent 也做多点检查:启动前检查 delegated task,运行中每个动作走同样规则,结束时还会审查完整动作历史。(Claude Code) OpenClaw 也支持每个 agent 覆盖 sandbox 和 tool policy,并提醒每个 agent 有自己的 auth store,不应复用
agentDir。(OpenClaw)十四、RAG 场景下的工具安全:检索内容必须当作不可信输入
在 RAG + Agent 中,工具调用常被间接 Prompt Injection 劫持:
如果 Agent 同时拥有
web_fetch、read、bash/curl,就可能把外部网页里的恶意文本当成新指令执行。防御方式:
检索内容只作为 data,不作为 instruction。RAG 文档进入模型前加来源标签和不可信边界。外部内容不能改变权限。工具结果回流前做注入扫描。高危工具调用必须引用用户原始意图,而不是引用网页指令。网络读取工具和本地 secret 读取工具不要同时给同一个低信任 Agent。
十五、生产可用的安全基线
一个企业级 Agent 工具安全基线可以这样配置:
同时配套:
dev container / Docker / VM 沙箱。出网 allowlist。secret 不进入模型上下文。.env、SSH、cloud credentials 默认 deny。所有工具调用落审计日志。对bypass / yolo / dangerously skip permissions这类模式,只允许在无生产凭证、无宿主挂载、无外网的隔离环境中使用。Claude Code 也明确提醒bypassPermissions会跳过权限提示和安全检查,应只在容器、VM、dev container 等隔离环境使用。(Claude Code)
十六、最终总结:工具调用安全的 7 条铁律
1. 权限在模型外执行。 Prompt 不是安全边界。2. 默认最小权限。 没明确需要的工具不暴露。3. deny 优先。 任意层级 deny 都不能被项目配置、子 Agent 或 Hook 放开。4. 开放式工具要拆窄。 少给bash,多给run_tests、read_docs、create_pr_draft。5. 高副作用动作必须 ask。 发邮件、推代码、部署、删库、改 IAM 都要人工确认。6. 沙箱是第二道防线。 工具策略防“能不能调用”,沙箱防“即使调用了能碰到什么”。7. RAG 和工具结果都不可信。 外部内容不能提升权限,不能覆盖系统策略,不能直接驱动敏感工具。
一句话概括:
成熟 Agent 的安全控制,不是让模型“学会听话”,而是让运行时具备“即使模型不听话也做不了坏事”的能力。
分享
