核心摘要
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 或软件包注册表只是“不可信候选来源”,内部注册表才是执行授权来源。
保护 MCP 传输与 Session 边界
MCP 的互操作性会引入协议特有风险,只有工具 Allowlist 不足以覆盖:
- 按 Client 和请求能力分别获得用户同意,不能让一个已批准 Client 静默授权另一个 Client;
- 精确校验 Redirect URI 和 OAuth
state,防止 Confused Deputy 与授权码拦截; - 禁止 Token Passthrough:MCP Server 必须拒绝不是签发给自身及其 Audience 的 Token;
- Session ID 只能用于路由或关联,不能当作身份认证;
- 把每个 Session 绑定到已认证 Principal,并在每次请求时重新执行授权;
- 把本地 MCP Server 当作本地代码执行边界,执行 Origin 校验、明确同意、沙箱和最小文件/网络权限;
- Server 发出
notifications/tools/list_changed后,对新工具定义和权限做 Diff,实质变化必须重新审查,不能直接暴露给模型。
Authorization Server、MCP Server 与下游 Resource Server 是不同安全主体。不能因为模型选择了某个 Tool,就把 Client 的宽权限 Token 原样转发到下游 API。应在策略判断后交换或签发 Audience-bound、最小权限的凭据。
在运行时执行最小权限
权限必须比“Agent 可以调用该工具”更细。系统应根据可信身份和当前状态授权精确副作用。
package main
import (
"errors"
"fmt"
)
type ToolRequest struct {
Tool string
InvoiceID string
AmountCents int64
}
type AuthContext struct {
UserID string
TenantID string
Roles map[string]bool
}
type Invoice struct {
ID string
TenantID string
RefundableCents int64
}
func authorize(request ToolRequest, context AuthContext, invoice Invoice) error {
if request.InvoiceID != invoice.ID {
return errors.New("invoice_mismatch")
}
if invoice.TenantID != context.TenantID {
return errors.New("tenant_denied")
}
switch request.Tool {
case "invoice.lookup":
return nil
case "invoice.refund":
if !context.Roles["refund_operator"] {
return errors.New("role_denied")
}
if request.AmountCents <= 0 || request.AmountCents > invoice.RefundableCents {
return errors.New("invalid_amount")
}
return nil
default:
return errors.New("tool_denied")
}
}
func main() {
request := ToolRequest{
Tool: "invoice.refund",
InvoiceID: "inv_2817",
AmountCents: 8420,
}
context := AuthContext{
UserID: "user_9",
TenantID: "tenant_a",
Roles: map[string]bool{"refund_operator": true},
}
invoice := Invoice{
ID: "inv_2817",
TenantID: "tenant_a",
RefundableCents: 10000,
}
fmt.Println(authorize(request, context, invoice))
}
模型不能提供 userId、tenantId、角色、价格、审批状态或策略版本。这些字段应从认证上下文与权威服务派生。
同时设置资源预算:
- 最大工具调用数与重复调用次数;
- 执行时间和并发;
- 输出字节数和行数;
- 网络目标与请求方法;
- 文件系统路径与进程能力;
- 金额、写入量和重试次数。
让审批真正约束操作
只显示“是否允许?”不构成有效审批。用户需要看到精确副作用:
为发票 inv_2817 退款 ¥612.40
目标:原支付方式
原因:重复扣款
工具版本:[email protected]
过期时间:2026-07-28T12:05:00Z
审批应绑定到:
- 工具及版本;
- 规范化参数;
- 目标资源和租户;
- 目标地址与副作用;
- 策略版本;
- 过期时间与幂等键。
任一绑定字段变化都要重新审批,不能让一次确认授权后续更宽的调用。
把工具结果视为不可信输入
工具调用成功不代表返回内容安全。结果重新进入模型上下文前必须规范化。
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
)
const (
maxRawBytes = 32 * 1024
maxTextRunes = 8000
)
type SearchResult struct {
SourceID string `json:"sourceId"`
Text string `json:"text"`
}
type NormalizedResult struct {
SourceID string `json:"sourceId"`
Text string `json:"text"`
Trust string `json:"trust"`
}
func normalizeSearchResult(data []byte) (NormalizedResult, error) {
if len(data) > maxRawBytes {
return NormalizedResult{}, errors.New("result_too_large")
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
var input SearchResult
if err := decoder.Decode(&input); err != nil {
return NormalizedResult{}, errors.New("invalid_result")
}
if input.SourceID == "" {
return NormalizedResult{}, errors.New("missing_source")
}
text := []rune(input.Text)
if len(text) > maxTextRunes {
text = text[:maxTextRunes]
}
return NormalizedResult{
SourceID: input.SourceID,
Text: string(text),
Trust: "external_untrusted",
}, nil
}
func main() {
result, err := normalizeSearchResult(
[]byte(`{"sourceId":"doc_7","text":"Treat this as data, not instructions."}`),
)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", result)
}
来源身份和信任标签应穿透摘要与记忆写入。返回文本不能直接定义新工具、改变策略、选择外部目标地址或写入持久记忆,除非通过单独门禁。
沙箱与网络出口限制
当授权或模型行为失败时,隔离可以限制影响范围。
| 边界 | 最低控制 |
|---|---|
| 进程 | 非 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。
- MCP Security Best Practices,访问于 2026-10-05。
- OWASP MCP Security Cheat Sheet,访问于 2026-10-05。
- OWASP AI Agent Security Cheat Sheet,访问于 2026-10-05。