7.3 工具调用安全

智能体通过函数调用(Function Calling)和工具使用扩展其能力边界,但这也带来了新的安全风险。

7.3.1 工具调用机制

现代 LLM 平台支持通过结构化方式调用外部工具和 API。

调用流程

spinner

图 7-12:工具调用机制时序图

工具定义示例

{
  "name": "send_email",
  "description": "发送邮件给指定收件人",
  "parameters": {
    "type": "object",
    "properties": {
      "to": {"type": "string", "description": "收件人邮箱"},
      "subject": {"type": "string", "description": "邮件主题"},
      "body": {"type": "string", "description": "邮件正文"}
    },
    "required": ["to", "subject", "body"]
  }
}

7.3.2 工具调用风险

参数注入

如果参数未经验证直接传递给后端系统,可能导致注入攻击。

工具滥用

风险
描述
示例

越权调用

调用不应被访问的工具

访问管理功能

参数篡改

修改应受限的参数

更改收款账户

批量滥用

大量调用消耗资源

批量发送邮件

链式攻击

组合多个工具实现攻击

收集信息后发送

7.3.3 参数验证不足

工具参数来源于 LLM 的输出,可能受到攻击者影响。

问题场景

spinner

图 7-13:参数验证不足流程图

防护不足的示例

7.3.4 工具权限设计

权限分级

spinner

图 7-14:工具权限设计流程图

权限控制要素

要素
描述

能力限制

限制可调用的工具集合

参数约束

限制参数取值范围

频率限制

限制调用频率

范围限制

限制操作对象范围

确认机制

高风险操作需确认

7.3.5 LLM 幻觉与工具参数生成

虽然幻觉是 LLM 的固有特性,但在工具调用的场景中,幻觉可能产生严重的安全后果。

幻觉生成无效或危险参数

LLM 可能在没有充分依据的情况下幻觉生成工具参数:

  • 虚构不存在的文件路径(导致创建/删除错误位置的文件)

  • 生成不存在的 API 端点或数据库表名(导致调用失败或信息泄露)

  • 指定超出权限范围的操作对象

  • 返回格式不符合工具预期的参数值

关键风险:不可逆操作的执行错误

对于具有持久化副作用的工具(如文件删除、数据修改、资金转账),幻觉生成的参数可能导致:

  • 删除错误的文件或数据库记录

  • 向错误的邮箱地址发送敏感信息

  • 在错误的账户之间进行资金转账

这些错误通常具有不可逆性,造成真实伤害。

检测与防护

  • 参数合理性检查:验证参数是否指向存在的对象(文件是否存在、数据库表是否有效等)

  • 操作前确认:对所有高风险操作(DELETE、TRANSFER、SEND),在执行前明确向用户展示将要操作的目标对象,要求用户确认

  • 幻觉检测:利用多轮验证、交叉引用等技术识别可疑参数,询问 LLM 参数的来源和合理性

  • 权限校验:确保 LLM 生成的参数对应的操作在当前上下文的权限范围内

  • 审计与恢复:完整记录所有工具调用,支持操作回滚和恢复机制

工具返回的结果也可能包含恶意内容。

返回值注入

spinner

图 7-15:工具返回值安全时序图

示例场景

7.3.6 工具链安全

多个工具协同工作时,攻击面进一步扩大。

链式攻击

spinner

图 7-16:工具链安全流程图

断链防护

  • 工具之间的数据传递需要验证

  • 敏感数据不应在工具链中流转

  • 每个工具独立进行安全检查

7.3.7 安全工具设计原则

原则一:最小能力

原则二:显式参数

原则三:安全默认

原则四:不信任输入

以下示例演示了对文件路径进行规范化与目录约束,避免路径穿越。

原则五:操作审计

7.3.8 MCP 生态下的工具安全

随着跨工具生态的发展,越来越多智能体通过 Model Context Protocol(MCP) 连接外部能力。 这会把风险从”单工具调用”扩展为”协议级信任链”问题。 (参考附录 C-38、C-39)

新增风险点

  • 恶意 MCP Server 提供被污染的工具描述或返回值

  • 客户端对工具能力声明(capability)验证不足

  • 认证信息在跨服务调用中泄露或滥用

  • 工具目录被投毒,导致”误接入”高风险服务

防护建议(协议层)

  1. 仅接入可信 MCP Server,启用来源校验与签名验证。

  2. 对每个工具定义强约束 schema,禁止自由格式高危参数。

  3. 将”可调用工具集合”与用户/任务上下文绑定,默认拒绝。

  4. 使用短期令牌和最小作用域凭证,不复用长期高权限密钥。

  5. 对 MCP 流量做审计和回放,支持事后追踪与封禁。

MCP 认证与授权:OAuth 2.1 实施细节

MCP 协议在资源服务器的身份验证和授权中应采用 OAuth 2.1 标准,实现更加严格的安全性保证:

关键安全机制

  • PKCE(Proof Key for Code Exchange):强制使用动态代码验证器,防止授权码被截获后的重放攻击

  • Bearer Token 生命周期管理:分布式 Agent 环境中,Token 有效期应严格控制在分钟级,支持在线验证和快速撤销

  • 受保护资源元数据:MCP Server 应在连接时明确声明”此工具需要何种权限范围”,Client 可据此做动态权限裁剪

防护实施

工具链权限提升(Privilege Escalation)防御

当多个工具协同工作时,攻击者可能通过精心排列工具调用顺序实现”跳级”权限获取,这被称为 工具链提升(Tool-Chain Escalation)

典型场景

防御对策

  • 工具链隔离:不同权限等级的工具之间禁止直接数据传递,所有跨等级调用必须通过认证网关

  • 单调性检查:系统应检测权限递增的工具序列模式,高风险链路需要用户显式确认

  • 凭证不流转:工具 B 不应直接返回可用凭证给工具 C,而应返回”使用凭证的操作结果”

7.3.9 Policy-as-Code 与双重网关

单靠 Prompt 规则无法稳定约束真实执行面。建议采用“策略即代码”:

落地要点

  • 在策略网关中定义“谁、在什么条件下、可调哪些工具、参数边界是什么”。

  • 在执行网关中做最终校验(参数白名单、速率、审批、审计)。

  • 对高风险动作(转账、删除、外发)要求二次确认或多人审批。

工具调用安全是智能体安全的核心组成部分。设计安全的工具 API 与协议边界控制,是构建可信智能体系统的基础。

7.3.10 案例研究:Claude Code 的工具安全模型

Claude Code 作为 Anthropic 官方的 Claude 命令行工具,体现了工业级智能体工具安全设计的最佳实践。其工具安全架构涵盖从 AST 白名单、多层检查、权限分类到卧底防御的完整防线。

Bash 安全:Fail-Closed 的 AST 白名单哲学

传统的安全工具采取"黑名单"策略——列举已知危险命令(如 rm -rfdd),然后拒绝执行。这种方法天生有缺陷:黑名单永远不完整,且 Bash 语法复杂,攻击者总能找到绕过方式。

Claude Code 采用完全相反的 Fail-Closed 设计

这意味着:

  • 只有经过验证的 Bash 语法结构才能自动执行

  • 任何未被明确列为安全的模式都会被标记,即使它看似无害

  • 用户最终保持对系统命令执行的控制权

这种策略的优势在于可证明的安全性:不依赖于对所有攻击向量的穷举性了解,而是保证"已知安全集合外的任何内容"都需要用户干预。

23 类安全检查及其对应的攻击向量

Claude Code 的 Bash 解析器实现了 23 类细粒度的安全检查,每一类都对应一种已知的攻击模式:

引号与注释同步失效(COMMENT_QUOTE_DESYNC)

这个命令看似安全(注释后的内容被忽略),但如果引号状态与注释检测失序,注释可能被解释为执行代码。Claude Code 单独追踪引号嵌套与注释位置,确保它们始终同步。

大括号扩展(BRACE_EXPANSION)

虽然看起来是安全的集合表示法,但大括号扩展是 Bash 的一个强大特性,可被滥用来构造多个命令。

Unicode 空白字符(UNICODE_WHITESPACE)

某些 Unicode 字符(零宽空格、零宽连字符等)在人类可读的输出中完全隐形,但 Bash 将其视为分隔符。Claude Code 检测并拒绝非 ASCII 空白。

字段分隔符注入(IFS_INJECTION)

IFS(Internal Field Separator)变量控制字段如何分割。恶意改变它可以导致参数被意外分割,从而执行不同的命令。

变量作用域精确追踪

Claude Code 对变量作用域的追踪比简单的线性假设更精细:

  • 线性作用域&&;):变量赋值对后续命令可见

  • 快照作用域|||&):后台进程、管道、条件分支使用变量的快照副本,不会看到后续的赋值

这种精确追踪防止变量通过复杂的控制流泄露到不应该接收它们的地方。

占位符防御机制

当命令包含动态元素时,Claude Code 采用一种独特的防御——将动态部分替换为占位符,然后标记为"过于复杂",要求用户确认:

具体的占位符规则:

  • $() 命令替换 → __CMDSUB_PLACEHOLDER__

  • 追踪变量引用 → __TRACKED_VAR__

  • 任何包含占位符的参数 → 命令标记为"太复杂"

这个机制防止模型通过精心构造看似无害、实际上包含动态攻击负载的命令来绕过防御。

权限分类器(YOLO)——两阶段 ML 决策

并非所有命令都需要人类审查。Claude Code 使用一个 两阶段的机器学习分类器来自动批准大多数安全命令:

Stage 1:快速、低成本的初步判断

  • 仅分析 tool_use 块(不受模型的自然语言输出影响)

  • 使用最小化的 token 预算(~64 tokens)

  • 不进行思维链(CoT)推理

  • 预期准确率:90%+ 的调用被标记为"安全"

Stage 2:只在 Stage 1 说"阻止"时激活

  • 完整的链式思考推理

  • 更大的上下文窗口

  • 更高的精确度,但成本更高

这种设计的关键优势:

  • 延迟优化:从800ms 下降到200ms(绝大多数安全调用)

  • 反操纵设计:分类器只看工具调用块,看不到模型的文本输出,防止模型通过说服性的文本来影响分类器的决定

四种权限模式支持不同的工作流:

  • default(交互模式):所有决策需用户确认

  • auto(ML 分类器):让分类器自动决定

  • bypass:权限手动授予一次

  • yolo:最高信任模式(仅在完全可控的环境中使用)

卧底模式(Undercover Mode)

在开源贡献、公共仓库或社区协作中,Claude Code 需要隐藏其身份,防止在公开代码中暴露自己是 AI 工具的迹象(这可能被利用进行针对性攻击)。

对称的成本与不对称的防御

  • 可以强制启用:CLAUDE_CODE_UNDERCOVER=1

  • 无法强制关闭(没有关闭开关)

  • 暴露身份的成本:安全风险

  • 不必要隐藏的成本:轻微的不便

这是一个典型的"防守不对称"设计——让隐藏身份成为默认的、单向的选择,不可被绕过。

容器安全与客户端认证

Claude Code 在容器化环境中运行时采取多层防御:

原生客户端认证

  • 通过计算哈希对客户端进行认证

  • 防止未授权的本地进程冒充合法客户端

内存保护

设置 PR_SET_DUMPABLE 标志,防止调试器或其他进程通过 ptrace 系统调用读取运行中的 Claude Code 进程的内存,从而获取敏感信息。

API 端点隔离

  • 某些端点(如重要的认证或敏感操作端点)被明确排除在代理(proxy)之外

  • 确保敏感调用绕过任何中间代理,直接到达目标服务

这些措施共同构成了一个纵深防御体系,确保即使某一层被突破,其他层仍能维持安全边界。

小结

Claude Code 的工具安全设计展现了以下关键原则:

  1. 白名单 + Fail-Closed:不依赖于穷举性的威胁清单,而是定义已知安全的子集

  2. 多层检查:23 类细粒度检查覆盖已知的攻击向量

  3. 动态内容隔离:占位符机制防止动态参数造成的绕过

  4. 智能决策分类:两阶段 ML 分类器在安全性与性能之间取得平衡

  5. 不对称的防御设计:某些安全选择是单向的、不可绕过的

  6. 纵深防御:从解析层、策略层到容器层的多重防线

这套架构可为其他工具调用系统的设计提供参考,尤其是在构建需要直接执行系统命令的智能体场景中。

最后更新于