逐日AI
第 1 周 · D1约 3 小时

威胁模型:致命三件套、信任边界,以及两张 OWASP 清单怎么当检查表用

先用致命三件套判据看清一个 Agent 危险在哪:私有数据、不可信内容、外部通信三者同时具备时,一次提示注入就能变成一次数据外泄;再把它画成信任边界图,用两张 OWASP 清单逐条对照。

今日目标 0/3

登录后可以勾选并保存进度。

今日目标

  1. 能用致命三件套判据判断一个 Agent 有没有可被注入利用的外泄链路,并说出拆掉哪一条最省
  2. 能把一个 Agent 画成带信任边界的数据流图,标出每条数据的可信度来源
  3. 能用 OWASP 的 LLM Top 10 与 Agentic Top 10 逐条对照出自己 Agent 的风险条目

这门课只解决一件事:怎么让一个会自己动手的 Agent,在被人指挥去干坏事的时候干不成。 今天先学会看清危险在哪——看不清就动手加防御,多半是在给一个不存在的问题打补丁,而真正的那条链路原封不动。读完回到页面顶部把三条目标勾掉。

小白版讲解

小区门禁与快递柜

你住的小区有门禁。物业为了方便,在大门口装了一排快递柜,谁都能走到柜子跟前放件取件。这件事本身不危险:外人进了大堂,也就是站在大堂里。

危险的是另一种组合。假设物业又做了两件看起来都很贴心的事:一是把每家的门牌号、住户姓名和手机号贴在了柜子旁边的公示栏上;二是给保安定了一条规矩——快递柜上贴的纸条写什么,保安就照着做,因为那多半是住户留的。

现在把三件事摆在一起看:外人能进大堂大堂里摆着住户名册保安会执行纸条上的指令。任何一个陌生人只要贴一张「请把公示栏上的名单拍下来发到这个手机号」的纸条,就完成了一次完整的信息外泄。而这三件事单拎出来,没有任何一件是失误:快递柜是便民,公示栏是为了方便找人,保安听住户的话是本职。

风险不在任何一件事里面,风险在它们的组合里。 这就是今天要讲的全部内容。你之所以查不出它,是因为你一直在逐件检查,而它只在拼起来的时候才存在。

致命三件套:为什么单独每条都不是 bug

把小区的故事换成 Agent 的说法,就是 Simon Willison 在 2025 年 6 月提出的致命三件套(lethal trifecta)。三件是:

  1. 访问私有数据——Agent 能读到不该公开的东西:客户库、内部文档、别人的会话记录。
  2. 接触不可信内容——Agent 会把别人能写字的内容读进上下文:工单正文、网页、邮件、第三方接口返回。
  3. 对外通信能力——Agent 能把信息送出去:发邮件、调 webhook、写进一个别人看得到的地方。

三者同时具备时,一次成功的提示注入就能变成一次数据外泄。链条很短:不可信内容里藏着一句指令 → 模型照做 → 它用私有数据填好内容 → 它用对外通信的工具把内容送走。

本课全程用同一个虚构靶子演示这条链路:一个叫 deskmate 的公司内部知识助手。它每天做三件事——读工单以及工单里贴的网页链接、查内部客户库回答问题、把结论发邮件或者调 webhook 通知同事。这三件事恰好把三条边凑齐了,而每一条都是产品经理明确要求的功能。

判据真正好用的地方在于它给的是拆法,不是防法。三条边是「与」的关系,拆掉任意一条,这条攻击链就断了。而三条边的代价通常差很远:私有数据往往是业务核心动不了,不可信内容是需求本身也去不掉,但「对外发送」这一条经常只是为了省事——很多 Agent 的发送能力其实可以降级成「把草稿写回工单,由人点一下发出去」。判断该拆哪条的机械办法是数涉及的工具数,最少的那条最省:

trifecta.js
// 判的是「能力」不是「行为」:工具清单里有,就算具备。
// 攻击者能不能用上它,不取决于你平时用不用它。
export function assessTrifecta(config) {
  const privateData = config.tools.filter((t) => t.sensitivity === 'private').map((t) => t.name)
  const untrusted = config.tools.filter((t) => t.returnsUntrusted).map((t) => t.name)
  // 不可信内容有两个来源:工具返回的外部内容,以及用户输入本身
  if (config.userInputTrust === 'untrusted') untrusted.push('<用户输入>')
  const externalComms = config.tools.filter((t) => t.effect === 'send').map((t) => t.name)
 
  const edges = [
    ['私有数据访问', privateData],
    ['不可信内容', untrusted],
    ['外部通信', externalComms],
  ]
  const complete = edges.every(([, tools]) => tools.length > 0)
  // 涉及工具数最少的那条边,就是最容易拆的那条
  const cheapest = [...edges].sort((a, b) => a[1].length - b[1].length)[0][0]
  return { privateData, untrusted, externalComms, complete, cheapest }
}

注意判定的是能力而不是行为。「我们的 Agent 从来不往外发客户资料」不构成理由——攻击者用得上的是工具清单,不是你的使用习惯。

直接注入与间接注入:攻击者不需要跟你的 Agent 说话

提示注入(prompt injection)的原理在 30 天课的 第 22 天 已经讲过一遍:模型收到的上下文最后会被拼成一整片扁平文本,你写的系统提示和别人写的内容在模型眼里只是先后到达的两段字,没有哪一段自带不可篡改的印章。所以它不是一个能被彻底修好的 bug,业界的共识是「假设它一定会成功,然后让它成功了也没用」。

对威胁建模来说,需要往前推一步的只有一句:直接注入是攻击者自己跟你的 Agent 说话,间接注入是攻击者把话写进一份你的 Agent 迟早会去读的东西里。后者才是真正难办的那种——他既不需要有你系统的账号,也不需要出现在你的日志里,他只要在一个工单的备注字段里、或者一个会被 fetch_page 抓到的网页角落里留下一段文字就够了。凡是 Agent 会读进上下文的地方,都是攻击面,这一条直接决定了下一节的信任边界怎么画。

画信任边界:给每条数据标上可信度

威胁建模的第一张图不是架构图,是数据流图加一条虚线。虚线两侧的区别只有一个:里面的东西是你能担保的,外面的东西是别人能写的。

画法只有三步:把所有会往上下文里灌东西的来源列出来,给每个来源标 trusted 或 untrusted,然后把所有能产生外部副作用的出口列出来。画完之后看有没有一条线能从 untrusted 的入口,一路走到某个出口。deskmate 的图长这样:

用户提问 untrusted deskmate 上下文 read_ticket 工单正文 untrusted fetch_page 网页正文 untrusted search_customers 客户库 private send_email 出口 post_webhook 出口 update_ticket 出口
Mermaid 源码
mermaidmermaid
flowchart LR
  U[用户提问 untrusted] --> A[deskmate 上下文]
  T[read_ticket 工单正文 untrusted] --> A
  W[fetch_page 网页正文 untrusted] --> A
  C[search_customers 客户库 private] --> A
  A --> E[send_email 出口]
  A --> H[post_webhook 出口]
  A --> K[update_ticket 出口]

这张图最值得盯的不是方框,是箭头的方向。三条 untrusted 的箭头和一条 private 的箭头汇进同一个上下文,然后从这个上下文分出三条通往外部的箭头——这个「汇进来再分出去」的形状本身就是警报。一旦不可信内容和私有数据在同一块上下文里相遇,而这块上下文又连着出口,模型就成了那个照着纸条办事的保安。

两个新手最容易画错的地方。第一,工具返回值也是入口。 很多人只把用户输入标成 untrusted,忘了 fetch_page 抓回来的网页正文是完完全全由陌生人书写的。第二,出口不只是「发消息」。 任何能被外部观察到的写操作都算出口:写回工单会被提交人看到,把内容拼进一个 URL 去请求,即使那个请求失败了,域名和路径也已经躺在对方的日志里了。数据外泄的出口通常比你列的清单长。

两张 OWASP 清单的分工

图画完了,接下来要回答的是「除了这条最大的链路,还有什么我没想到的」。这时候需要检查表,OWASP 正好维护着两张。

OWASP Top 10 for LLM Applications 看的是模型应用:输入进去、输出回来这一段里会出什么问题。今天要用到的三条是 LLM01 提示注入、LLM02 敏感信息泄露、LLM06 过度代理权。

OWASP Top 10 for Agentic Applications(2025 年 12 月 9 日发布,编号 ASI01 到 ASI10)看的是会自己动手的 Agent:它有循环、有工具、有记忆、可能还有别的 Agent 同事,这些都是 LLM 清单覆盖不到的。今天要用到的四条是 ASI01 目标劫持、ASI02 工具滥用、ASI03 身份与权限滥用、ASI06 记忆与上下文投毒。

分工可以用一句话记住:LLM 清单管「模型说了什么」,ASI 清单管「Agent 做了什么」。 一个只会回答问题的客服机器人,用 LLM 清单基本够了;一个能调工具、能改状态、能跨轮次记住东西的 Agent,缺了 ASI 这张就会漏掉整整一类风险——比如同一条注入在单次问答里只是让模型胡说八道,在有记忆的 Agent 上会被写进长期记忆,之后每一轮都生效,这就是 ASI06。

清单的正确用法是逐条问自己「这条在我这儿长什么样」,而不是逐条打钩说「我们没有这个问题」。对 deskmate 来说,LLM01 长成「工单备注字段」,ASI02 长成「send_email 的收件人由模型自由填写」。写不出具体形态,就说明你还没想清楚。

产出不是文档,是一张能逐条打勾的风险表

威胁建模最常见的失败形态是产出一份散文:写得四平八稳,谁也挑不出错,但没人能拿它做任何事,三个月后也没人会再打开它。

避免这件事的办法是给每条风险规定字段,其中最关键的是证据:是配置里的哪几个工具让这条风险成立的。有证据的风险条目可以被当面质疑(「你说的这个工具我们上个月已经下掉了」),没证据的只能被点头略过。

risks.js
// 每条风险必须回答三件事:严重度、对应的 OWASP 编号、凭什么说它成立。
export function buildRiskItems(config, verdict) {
  const items = []
  if (verdict.complete) {
    items.push({
      id: 'R-TRIFECTA',
      title: '致命三件套齐全:一次间接注入即可完成数据外泄',
      severity: 'critical',
      owasp: ['LLM01 提示注入', 'LLM02 敏感信息泄露', 'ASI01 目标劫持'],
      evidence: [
        `私有数据:${verdict.privateData.join('、')}`,
        `不可信内容:${verdict.untrusted.join('、')}`,
        `外部通信:${verdict.externalComms.join('、')}`,
      ],
      mitigation: '拆掉任意一条边;拆不掉就把外部通信收进确认门与出口白名单',
    })
  }
  for (const tool of config.tools) {
    if (tool.effect === 'send' && !tool.reversible) {
      items.push({
        id: `R-IRREVERSIBLE-${tool.name}`,
        title: `${tool.name} 是不可逆的对外动作,出错没有第二次机会`,
        severity: 'high',
        owasp: ['LLM06 过度代理权', 'ASI02 工具滥用'],
        evidence: [tool.description],
        mitigation: '加确认门,并限制收件人与目标地址的白名单',
      })
    }
  }
  return items
}

这段代码就是今天实验的核心。它有意写得很笨——全是 if 判断,没有模型参与。威胁建模是一件在写代码之前就该做完的事,不需要把 Agent 跑起来,也不该依赖一个模型来告诉你自己的系统有什么风险。

还有一个字段值得单独说:严重度不是拍脑袋。同一个「工具返回不可信内容」,在三件套齐全的 Agent 上是 high,在没有对外通信能力的 Agent 上就降成 medium——因为后者被劫持了也送不出去。严重度是链路的属性,不是单点的属性,这一点和前面「拆一条边」是同一条道理的两种说法。

五天分别在哪一层下手

先把这门课的立场亮出来,后面四天每一天都会回到这句话:

检测器不是边界,架构才是。

能被自适应攻击击穿的东西不叫防线,只叫过滤器。真正的边界是「不可信输入进来之后,这个 Agent 在结构上就做不到那件坏事」。五天的顺序就是按「离这条判据有多近」排的:

在哪一层下手一句话
D1判断层看清三条边在哪,算出拆哪条最省
D2输入层四档输入侧防御各自能挡住多少、上限在哪
D3架构层六个设计模式,让攻击成功率归零而任务完成率保住
D4运行时层策略引擎、出口白名单、用户级凭据、审计日志
D5流程层红队用例集、阈值门禁、断路开关与事故响应

从第二天起,每一天都要同时报两个数字:ASR(攻击成功率)和 utility(受攻击下的任务完成率)。只报前一个的方案一律不可信——把 Agent 改成什么都不做,ASR 立刻归零,utility 也一起归零。

源码导读

动手实验

🧪 D1 实验:威胁建模器

代码位置:labs/agent-security-5days/day-01-threat-model

验收标准:

  1. deskmate 的判定是「三件套齐全」,三条边下面各自列出了对应的工具名
  2. 风险条目里第一条是 [CRITICAL] R-TRIFECTA,且它的 evidence 列出了三条边各自的工具
  3. 条目总数是 6 条,且严重度从高到低排列
  4. 对照组 deskmate-no-send 的判定翻转成「三件套未齐全」,条目掉到 3 条且没有 critical
  5. pnpm typecheck 退出码为 0

这个实验纯本地计算,不连模型也不联网——今天要练的就是「在把 Agent 跑起来之前就能判断它危不危险」。动手之前先自己猜一遍:deskmate 的六个工具里,哪几个撑起了三条边?猜完再跑,对不上的那一条才是你今天真正的收获。卡住了先看 solution/src/model/ 里的注释,每个函数都写了为什么这么判。

  1. 跑通 solution 的 pnpm start,看清 deskmate 的三件套判定与那张按严重度排序的风险条目表。
  2. 回到 starter 补全 trifecta.ts 的三件套判定,注意用户输入本身也是一条不可信内容来源,补完确认三条边下面都列出了工具名。
  3. 补全 cheapestCutrisks.ts 的风险条目生成,让每条风险都落到 OWASP 编号并带上证据行。
  4. 看对照组 deskmate-no-send 的输出:只删掉两个发送工具,critical 条目就消失了,这就是「拆一条边」的直观演示。
  5. DESKMATEuserInputTrust 改成 trusted 再跑一遍,确认不可信内容那条边仍然成立,说明判定没有被单一字段绑架;最后换成你自己项目的工具清单跑一遍,把输出存下来。

面试题

今天 3 道题在下方题库区,侧重致命三件套判据、直接注入与间接注入的区别、以及信任边界该怎么在半小时内画出来。展开后先看「分析过程」再看要点——照着推导练,比背要点管用。标注「国内高频 / 海外高频」方便按目标市场取舍。

检查清单与明日预告

  • 能用致命三件套判据判断一个 Agent 有没有可被注入利用的外泄链路,并说出拆掉哪一条最省
  • 能把一个 Agent 画成带信任边界的数据流图,标出每条数据的可信度来源
  • 能用 OWASP 的 LLM Top 10 与 Agentic Top 10 逐条对照出自己 Agent 的风险条目
  • 能解释为什么代码审查、依赖扫描、单元测试都发现不了这类风险
  • 能说清严重度为什么是链路的属性而不是单点的属性
  • 实验的 5 条验收标准全部通过,并已经跑过一遍自己项目的工具清单
  • 3 道面试题不看要点也能答出至少 2 道

明天(D2)我们把靶子真的架起来:给 deskmate 搭一个注入靶场,从工单正文、网页正文、工具返回三条注入面分别打进去,然后依次上四档输入侧防御——分隔符、来源标记、聚光标注、检测器——每加一档就量一次 ASR 与 utility。先量再防,顺序和今天「先看清三条边再动手拆」是同一条:不知道现在被打穿多少次,就没法判断一个防御到底有没有用。你会看到第一档的两个数字和完全不设防一模一样,也会看到四档上齐之后只要换一条没见过的攻击用例,ASR 就又回来了——那两件事正是第三天要讲架构级防御的理由。

面试题库

  • 什么是致命三件套?为什么说它描述的是组合风险而不是单点缺陷?What is the lethal trifecta, and why is it a compositional risk rather than a single-point defect?
    国内高频海外高频基础#threat-modeling#lethal-trifecta

    分析过程 · 先想清楚再作答

    1. 这题在考你把安全当成「找 bug」还是当成「看结构」。能背出三条边的人很多,能说清「为什么现有的质量闸门一条都拦不住它」的人很少,区分度全在后半句。
    2. 先给三条边:访问私有数据、接触不可信内容、能对外通信。然后立刻补一句它们是「与」的关系——三者同时具备,一次成功的提示注入才能升级成一次数据外泄。
    3. 怎么拆「为什么是组合风险」:逐条问「这一条单独存在算不算缺陷」。查客户库是产品需求,读工单和网页是产品需求,发邮件通知也是产品需求,三条都不是 bug。风险只在拼起来的时候才出现,所以它不在任何一行代码里。
    4. 由此推出一个很有说服力的结论:代码审查、依赖扫描、单元测试都发现不了它——前两个看的是单段代码和单个依赖,第三个看的是单个函数的输入输出,而这条风险是工具清单的整体属性。
    5. 结论落到用法上:判据的价值是给拆法不是给防法。三条边拆掉任意一条链就断,而三条的代价通常差很远,多数系统里最便宜的一刀是把「自由对外发送」降级成「写回草稿由人点发送」。
    6. 可预期的追问:那你怎么判断该拆哪条?答机械办法——数每条边涉及的工具数,最少的那条最省;同时强调判的是能力不是行为,「我们从来不往外发客户资料」不构成理由,攻击者用得上的是工具清单。

    How to reason about it · think before answering

    1. This question separates people who hunt for bugs from people who read structure. Listing the three legs is easy; explaining why none of your existing quality gates catches it is where the signal is.
    2. Name the three legs first: access to private data, exposure to untrusted content, and the ability to communicate externally. Then state immediately that they are ANDed, not ORed, and that all three must hold before a successful prompt injection becomes data exfiltration.
    3. Show why it is compositional by testing each leg alone. Querying the customer database is a product requirement, reading tickets and linked web pages is a product requirement, and sending notifications is a product requirement. None is a defect, so the risk exists only in the combination and lives in no single line of code.
    4. That leads to a strong conclusion: code review, dependency scanning and unit tests all miss it. The first two inspect one snippet or one library, the third inspects one function's inputs and outputs, while this risk is a property of the tool inventory as a whole.
    5. Land it on how the criterion is used: it prescribes removal, not defense. Breaking any one leg breaks the chain, and the three legs rarely cost the same. In most systems the cheapest cut is downgrading free-form outbound sending to drafting something a human then sends.
    6. Expect the follow-up: which leg do you cut? Give the mechanical rule, cut the leg backed by the fewest tools, and stress that the test is capability, not habit. Saying you never send customer data out is not an argument, because the attacker gets the tool inventory, not your habits.

    答题要点

    • 三条边:访问私有数据、接触不可信内容、能对外通信;三者同时具备才构成完整的外泄链路。
    • 每一条单独看都是正常产品需求,没有任何一条是 bug,风险是组合出来的。
    • 因此代码审查、依赖扫描、单元测试都发现不了它——它是工具清单的整体属性,不在任何一行代码里。
    • 判据的用法是拆不是防:拆掉任意一条边攻击链就断,最便宜的通常是把自由对外发送降级成人工确认。
    • 判的是能力不是行为:工具清单里有就算具备,跟你平时用不用无关。

    Key points

    • Three legs: private data access, untrusted content, and external communication. Only all three together form a complete exfiltration path.
    • Each leg alone is a legitimate product requirement, not a defect. The risk is created by the combination.
    • That is why code review, dependency scanning and unit tests miss it: it is a property of the whole tool inventory, not of any single line of code.
    • Use it to remove, not to defend. Cutting any one leg breaks the chain, and the cheapest cut is usually turning free-form sending into a human-confirmed draft.
    • The test is capability, not behavior. If the tool is in the inventory, the capability exists regardless of how you normally use it.
  • 直接注入和间接注入有什么区别?哪一种更难防,为什么?What is the difference between direct and indirect prompt injection, and which is harder to defend against?
    国内高频海外高频进阶#prompt-injection#attack-surface

    分析过程 · 先想清楚再作答

    1. 这题的题眼在第二问。只答「间接注入更难防,因为更隐蔽」是营销话术,面试官想听的是「难在哪个具体环节」。
    2. 先给定义差:直接注入是攻击者自己跟你的 Agent 说话,载荷经过用户输入这个入口;间接注入是攻击者把话写进一份你的 Agent 迟早会去读的东西里——工单备注、网页正文、邮件、第三方接口返回。
    3. 怎么拆「难在哪」:分成三层说。第一层是入口数量,用户输入只有一个口子,而工具返回有多少个工具就有多少个口子,而且每加一个工具就多一个。第二层是身份,间接注入的攻击者不需要你系统的账号,也不会出现在你的访问日志里,事后溯源极难。第三层是时间差,载荷可以先躺在一个网页上,等哪天有人贴了这个链接才生效。
    4. 还要点出一个很多人漏掉的判据:凡是 Agent 会读进上下文的地方都是攻击面,包括你自己数据库里的字段,只要那个字段是外部用户填的。这一条决定了信任边界图上该把哪些箭头标成 untrusted。
    5. 结论:间接注入更难防,但难的不是检测而是归责——你连「谁写的这段话」都答不上来,所以防御必须落在结构上而不是落在识别攻击者上。
    6. 可预期的追问:那能不能把不可信内容都过滤一遍?要答出上限——注入不像 SQL 注入那样有明确的语法边界,自然语言里指令和数据长得一模一样,所以业界共识是假设它一定会成功,然后让它成功了也没用。

    How to reason about it · think before answering

    1. The hinge is the second half. Answering that indirect injection is harder because it is stealthier reads as marketing. The interviewer wants to know which concrete step gets harder.
    2. Start with the definitional difference. Direct injection means the attacker talks to your agent, so the payload enters through user input. Indirect injection means the attacker writes into something your agent will eventually read: a ticket comment, a web page, an email, a third-party API response.
    3. Break down the difficulty in three layers. First, entry count: user input is one channel, while tool returns give you one channel per tool and a new one with every tool you add. Second, identity: an indirect attacker needs no account and leaves no trace in your access logs, so attribution is nearly impossible. Third, timing: the payload can sit on a page for weeks until someone happens to paste that link.
    4. Add the criterion most people miss. Anything the agent reads into context is attack surface, including fields in your own database whenever those fields are filled in by external users. This is what decides which arrows get marked untrusted on the trust-boundary diagram.
    5. Conclusion: indirect is harder, and the hard part is attribution rather than detection. You cannot even say who wrote the text, so defenses have to be structural rather than attacker-identifying.
    6. Expect the follow-up: can you just filter untrusted content? Name the ceiling. Unlike SQL injection there is no syntactic boundary; in natural language instructions and data look identical, so the working assumption is that injection will succeed and the job is to make success worthless.

    答题要点

    • 直接注入走用户输入这个口子,攻击者自己跟 Agent 说话;间接注入把载荷写进 Agent 迟早会读的内容里。
    • 间接注入更难防:入口随工具数量增长、攻击者不需要账号也不进日志、载荷可以提前埋好等待触发。
    • 判据是「凡是会被读进上下文的地方都是攻击面」,包括自己数据库里由外部用户填写的字段。
    • 真正难的是归责而不是检测,所以防御必须落在结构上而不是落在识别攻击者上。
    • 过滤有上限:自然语言里指令和数据没有语法边界,注入不是一个能被彻底修好的 bug。

    Key points

    • Direct injection enters through user input; indirect injection plants the payload in content the agent will read on its own.
    • Indirect is harder: entry points scale with tool count, the attacker needs no account and leaves no log trace, and the payload can be planted long before it fires.
    • The working rule is that anything read into context is attack surface, including your own database fields when external users fill them in.
    • The genuinely hard part is attribution, not detection, so defenses must be structural rather than attacker-identifying.
    • Filtering has a ceiling: natural language has no syntactic boundary between instruction and data, so injection is not a bug that gets fixed.
  • 给你一个已经上线的 Agent,你怎么在半小时内画出它的信任边界并找出最高危的那条链路?Given an agent already in production, how would you map its trust boundary and find its highest-risk path in half an hour?
    国内高频海外高频深入#threat-modeling#trust-boundary

    分析过程 · 先想清楚再作答

    1. 这题在考流程而不是知识。会背三件套但给不出可执行步骤的人,到这一步就露馅了;面试官想确认的是你真的干过这件事。
    2. 先把范围钉死:半小时内不可能画完整架构图,所以只画三样东西——所有会往上下文里灌内容的入口、所有能读到私有数据的来源、所有能产生外部可观察副作用的出口。其余一律先不画。
    3. 怎么拆:入口和出口都从工具清单里读,不要靠问人。每个工具问两句话——它的返回值是不是外部可写的,它执行完之后外面有没有人能看见变化。两句话就能把一个工具归进入口、出口或者两者都不是。
    4. 这里要主动点出两个新手陷阱,能立刻拉开差距:一是只把用户输入标成不可信而忘了工具返回值,二是把出口理解成「发消息」——其实写回工单、拼进 URL 去请求一个外部地址,哪怕请求失败,域名和路径也已经进了对方的日志,这些都是出口。
    5. 结论:画完看有没有一条线能从不可信入口走到出口,并且中途经过私有数据。有就记一条 critical,处置写「拆哪条边最省」,判据是数每条边涉及的工具数。然后再用 OWASP 的两张清单逐条问「这一条在我这儿长什么样」,把漏网的补上。
    6. 可预期的追问:为什么是两张清单不是一张?答分工——LLM Top 10 管模型应用这一段,Agentic Top 10 管会自己动手的 Agent 才有的东西,比如记忆投毒和工具滥用。只会回答问题的机器人用前一张够了,能调工具改状态的 Agent 缺了后一张会整类漏掉。

    How to reason about it · think before answering

    1. This question tests process, not knowledge. Someone who can recite the trifecta but cannot give executable steps is exposed here; the interviewer wants evidence you have actually done it.
    2. Fix the scope first. Half an hour is not enough for a full architecture diagram, so draw only three things: every source that injects content into the context, every source that can read private data, and every exit that produces an externally observable side effect. Everything else waits.
    3. Derive entries and exits from the tool inventory rather than by asking people. For each tool ask two questions: can an outsider write what it returns, and after it runs can anyone outside see a change? Two questions classify every tool as entry, exit, both or neither.
    4. Call out two rookie traps, which immediately separates you from the pack. First, marking only user input as untrusted and forgetting tool return values. Second, reading exit as sending a message, when writing back to a ticket counts, and so does embedding data in a URL you fetch, since even a failed request leaves the domain and path in someone else's logs.
    5. Conclusion: look for a path from an untrusted entry to an exit that passes through private data. If one exists, log a critical item whose mitigation names the cheapest leg to cut, chosen by counting the tools behind each leg. Then walk both OWASP lists asking what each item looks like in this specific system.
    6. Expect the follow-up: why two lists? Explain the split. The LLM Top 10 covers the model-application layer, while the Agentic Top 10 covers what only an acting agent has, such as memory poisoning and tool misuse. A question-answering bot needs only the first; an agent that calls tools and changes state loses a whole class of risk without the second.

    答题要点

    • 限定范围:只画不可信入口、私有数据源、外部出口三样,不画完整架构图。
    • 从工具清单推导:每个工具问「返回值是不是外部可写」和「执行后外面看不看得见」两句话。
    • 两个易错点:工具返回值也是不可信入口;出口不只是发消息,写回工单和拼进 URL 的请求都算。
    • 找链路:有没有一条线从不可信入口经过私有数据走到出口,有就是 critical,处置写拆哪条边最省。
    • 最后用 LLM Top 10 与 Agentic Top 10 逐条问「这条在我这儿长什么样」补漏,产出是一张带证据的风险表而不是一份文档。

    Key points

    • Scope it: draw only untrusted entries, private data sources and external exits, not a full architecture diagram.
    • Derive them from the tool inventory by asking, per tool, whether outsiders can write its return value and whether its effects are externally visible.
    • Two common mistakes: forgetting that tool return values are untrusted entries, and treating exits as messaging only, when ticket writebacks and data-bearing URLs also qualify.
    • Look for a path from an untrusted entry through private data to an exit. If one exists it is critical, and the mitigation names the cheapest leg to cut.
    • Close with both OWASP lists, asking what each item looks like in this system, and deliver a risk table with evidence rather than a prose document.

评论