核心摘要
AI Agent 工具代表可执行权限,不是普通提示词扩展。生产系统应使用已验证注册表、最小权限、确定性授权、隔离执行、有界输入输出、精确操作审批和版本化审计。工具描述与结果都应视为不可信;模型拒答和系统提示词不能替代运行时策略。
Agent 工具调用中的信任边界
Agent 系统由两个控制面组成:
- 模型根据自然语言上下文提出要做什么;
- 可信软件判断是否允许并执行操作。
把“模型提议”误当成“已授权操作”,安全边界就会失效。
每条箭头都是信任边界。用户输入、检索文档、工具描述、调用参数、远程响应、记忆和委派 Agent 消息都可能改变下一步决策。
威胁模型:四条不同攻击路径
不要把所有事故都归为“提示词注入”。不同路径需要不同控制。
| 攻击路径 | 示例 | 主要控制 |
|---|---|---|
| 直接或间接提示词注入 | 网页要求 Agent 上传私有文件 | 能力限制、来源标记、出口策略 |
| 工具投毒 | 工具描述隐藏指令,或审查后改变行为 | 注册表验证、版本锁定、差异审查 |
| 函数调用后的工具滥用 | Agent 用合法删除 API 操作了错误资源 | 对象授权、预览、审批、幂等 |
| 恶意工具结果 | 返回内容含指令、秘密或超大负载 | 结果 Schema、大小限制、污点标记、内容隔离 |
OWASP Agentic Skills Top 10 强调发布者验证、权限清单、安全解析、依赖锁定、沙箱与运行时监控。本质上是把成熟软件供应链控制应用到由模型引导的执行层。
建立已审查工具注册表
动态发现不代表动态信任。发现的工具应转换为目标环境中经过审查的内部注册表条目。
{
"tool_id": "invoice.lookup",
"version": "3.2.1",
"artifact_digest": "sha256:...",
"publisher": "billing-platform",
"effects": ["read:invoice"],
"network_allowlist": ["billing.internal"],
"data_classes": ["customer_financial"],
"approval": "none",
"result_max_bytes": 65536
}
以下实质变化都要触发新一轮审查:
- 名称或描述;
- 参数或结果 Schema;
- 请求权限;
- 网络目标;
- 软件依赖或制品摘要;
- 副作用行为;
- 数据保留规则。
MCP Server、插件、Skill 或软件包注册表只是“不可信候选来源”,内部注册表才是执行授权来源。
在运行时执行最小权限
权限必须比“Agent 可以调用该工具”更细。系统应根据可信身份和当前状态授权精确副作用。
type ToolRequest = {
tool: "invoice.lookup" | "invoice.refund";
invoiceId: string;
amount?: number;
};
async function authorize(
request: ToolRequest,
context: { userId: string; tenantId: string; roles: string[] },
) {
const invoice = await loadInvoice(request.invoiceId);
if (invoice.tenantId !== context.tenantId) throw new Error("tenant_denied");
if (request.tool === "invoice.refund") {
if (!context.roles.includes("refund_operator")) throw new Error("role_denied");
if (!request.amount || request.amount > invoice.refundableAmount) {
throw new Error("invalid_amount");
}
}
return invoice;
}
模型不能提供 userId、tenantId、角色、价格、审批状态或策略版本。这些字段应从认证上下文与权威服务派生。
同时设置资源预算:
- 最大工具调用数与重复调用次数;
- 执行时间和并发;
- 输出字节数和行数;
- 网络目标与请求方法;
- 文件系统路径与进程能力;
- 金额、写入量和重试次数。
让审批真正约束操作
只显示“是否允许?”不构成有效审批。用户需要看到精确副作用:
为发票 inv_2817 退款 ¥612.40
目标:原支付方式
原因:重复扣款
工具版本:[email protected]
过期时间:2026-07-28T12:05:00Z
审批应绑定到:
- 工具及版本;
- 规范化参数;
- 目标资源和租户;
- 目标地址与副作用;
- 策略版本;
- 过期时间与幂等键。
任一绑定字段变化都要重新审批,不能让一次确认授权后续更宽的调用。
把工具结果视为不可信输入
工具调用成功不代表返回内容安全。结果重新进入模型上下文前必须规范化。
function normalizeSearchResult(value: unknown) {
if (!value || typeof value !== "object") throw new Error("invalid_result");
const input = value as Record<string, unknown>;
const text = typeof input.text === "string" ? input.text.slice(0, 8_000) : "";
return {
sourceId: String(input.sourceId ?? ""),
text,
trust: "external_untrusted",
};
}
来源身份和信任标签应穿透摘要与记忆写入。返回文本不能直接定义新工具、改变策略、选择外部目标地址或写入持久记忆,除非通过单独门禁。
沙箱与网络出口限制
当授权或模型行为失败时,隔离可以限制影响范围。
| 边界 | 最低控制 |
|---|---|
| 进程 | 非 root 身份、系统调用与资源限制 |
| 文件系统 | 只读基础层、显式可写目录 |
| 网络 | 默认拒绝、目标和协议允许列表 |
| 凭据 | 短期、工具专用、租户范围 |
| 数据 | 最少字段、脱敏、结果大小上限 |
| 副作用 | 幂等键、预览、交易额度 |
读工具与写工具应分开。如果可以提供狭窄业务操作,就不要暴露通用 Shell、SQL 执行器、HTTP Client 或云凭据。
测试完整攻击链
安全测试要覆盖调用轨迹,而不只是单轮提示词:
- 被投毒的工具描述要求执行未声明前置操作;
- 搜索结果要求外传数据;
- 工具返回其他租户标识符;
- 超时后模型重复写操作;
- 规划与执行之间工具版本变化;
- 委派 Agent 请求更宽凭据;
- 输出尝试把指令写入长期记忆。
每个案例都要断言阻止影响的确定性边界。模型“通常会拒绝”不能算安全测试通过。
遥测记录工具 ID 和版本、注册表摘要、认证主体、策略决策、审批引用、规范化参数摘要、目标类别、副作用结果、耗时和有界错误码。不要保存原始秘密、完整提示词和全部私有返回值。
事故响应与熔断
事故前就要准备:
- 全局禁用某个工具版本或发布者;
- 独立撤销工具凭据;
- 阻断某个目标地址或副作用类型;
- 隔离受影响执行写入的记忆;
- 按制品摘要定位历史调用;
- 使用脱敏 Fixture 重放策略决策;
- 通知外部可见副作用的责任人。
审计日志既要有调查价值,也不能变成第二套敏感数据仓库。低敏关联元数据与严格受控的负载证据应分开存储。
生产检查清单
- 盘点工具、效果、数据类别和外部目标。
- 把发现结果转换为已审查允许列表。
- 锁定制品并审查每次实质更新。
- 从可信上下文派生身份与租户。
- 用代码校验并授权精确副作用。
- 沙箱执行,网络出口默认拒绝。
- 审批绑定精确参数与版本。
- 规范化、限制并标记每个工具结果。
- 测试注入、投毒、重试、委派和记忆持久化。
- 维护熔断开关和事故查询。
提示词注入防御指南覆盖更广泛的不可信内容问题;函数调用指南解释模型提议到实际执行的边界。
常见问题
可以允许 Agent 自行安装工具吗?
不能直接安装到生产信任域。Agent 可以发现候选或生成变更申请,但安装、权限、制品验证和启用需要独立审查流程。
已签名工具一定安全吗?
不一定。签名只能证明相对于某个密钥的来源与完整性,不能证明发布者可信、代码安全、权限最小或返回内容无害。
输入输出分类器能阻止工具攻击吗?
分类器可以提供信号,但具有概率性且可被绕过。它们可以参与策略或复核,不能成为保护数据与副作用的唯一屏障。
写工具应如何重试?
使用幂等键和权威状态查询。出现不明确超时时,先查询操作结果再决定是否重试,不能让模型自行判断写操作可以重复。
有人工审批就足够安全吗?
只有当用户看到准确、有界的操作,并且审批与该精确操作绑定时才有效。模糊或过载的确认会导致审批疲劳,也不会真正收窄权限。
参考资料
- OWASP Agentic Skills Top 10,访问于 2026-07-28。
- OWASP Agentic Applications Top 10,访问于 2026-07-28。
- OWASP LLM06:Excessive Agency,访问于 2026-07-28。