技术 · 熟练
没人碰你的机器,它也能替你改一个动作
不崩、不报错、流程跑完了 —— 问题就在中间某一步
你给 agent 看了网页、读了 issue、跑了工具,它最后做的事却不完全是你交代的那件 —— 中间某一步被人插了一句话,而那句「话」本来只是要被读的数据。这类攻击叫间接提示注入:指令藏在它读到的内容里,不在你的输入框里。它最麻烦的地方是整个过程看起来是成功的 —— 不崩溃、不报错、任务跑完,只有一两步被悄悄改掉。这一页给的是拦它的位置与判据:先明白为什么「看完再评分」和「入口筛查」都够不着它,再把检查点放到每一次要产生对外效果的那一刻,用三个可核的问题筛一遍;最后用你自己环境里的真实工具输出,验证你这道门到底拦得住多少。
方法 · 一图看懂
1
先分清它从哪来 — 指令不在你的输入里,而在它读到的东西里:文档、issue、网页、别人的工具返回
2
把检查点放到「要动的那一刻」 — 入口筛查太早、跑完再评分太晚,唯一够得着的是每次动作前
3
三个问题筛一个动作 — 未被授权 · 有实质影响 · 攻击者可触及,三个都要成立才拦
4
串起来时会失效 — 每个 agent 各自拦得住,不代表它们之间传话时拦得住
5
用自己环境验证 — 榜单分数不算数,要用你自己的工具输出、在低误报率下看召回
原理 · 为什么有效
先看为什么常见的两种做法都够不着它。入口筛查的问题是时机太早:注入的内容到达时,还没跟「让它变致命的那段上下文」组合在一起 —— 一段「周五前把发布分支清掉」的文字,在刚被读取时和一条正常提醒没有区别,只有跟当前任务、当前权限拼起来才变成动作。跑完再评分的问题是时机太晚:等轨迹能被打分,效果已经落地,你拿到的是精准的尸检,不是拦截。
能同时解决时机与判据的位置只有一个:动作边界 —— agent 把内部状态变成对外效果的那一秒(删一条记录、发一条消息、推一次配置、把内容发出去)。在这个点上,「该不该做」不再取决于这次运行像不像被攻击,而取决于两件由可信任务(而不是这次运行)定下来的事:① 这个效果被授权了吗 ② 支撑它的运行时信息,被这个任务背书了吗。这样做的好处是它跟你面对的是哪种攻击无关 —— 换一种注入手法、换一个载体(工具 / MCP server / 技能包),判据不用改。
三个问题怎么用。把待执行的动作拆成它的操作要素(对哪个资源、做哪个动作、关键参数取什么值),逐个要素问三句:它有没有超出我这次交代的范围 · 它会不会造成实质影响 · 它的值是不是来自我不认识、也管不着的地方。三个都成立才拦 —— 这个条件的写法很重要:不是「看着可疑就拦」,而是「有一条可指认的、来自攻击者可触及处的、未被授权的实质影响」。写成后者,误拦才压得住,门才不会被绕过去关掉。
串起来的时候,保证会掉。有人会以为「每个 agent 都装了防护,链路就安全了」—— 实测不成立:不可信数据会被下游 agent 重新当成可信输入(上游把它当数据读完,下游把它当指令接住),于是每个环节都合规、整条链路失守。补法是把「指令」与「数据」分两条通道传,让来源跟着数据一起走,而不是靠每个环节各自猜。同一批实测里,这一改把攻击成功率从 12.9% 压到 0.0%;代价是效率略降,且模型越强代价越小。
最后一条最容易被忽略:你的检测手段本身也要被验证。同一批研究给了个很扫兴的结论 —— 检测器在公开榜上的名次,预测不了它在你的 agent 里表现如何:某个榜上最好的检测器,换到另一个 benchmark 上、在 1% 误报率下只抓到 2%;另一个能抓 72% 的,换个环境只剩 15%。而误报率却会转移(有的检测器在真实工具输出上的误报能超过 90%)。所以「我装了个检测器」不算数:要用你自己 agent 的真实工具输出、在你能接受的误报水平上看召回。
适用场景
适用:你让 agent 读外部内容并且它能产生对外效果 —— 读网页 / issue / 别人的文档后再去执行、跑自动化脚本、动数据库或生产配置、替你发消息。也适用:你要向别人解释「为什么我们非得在动的那一刻加一道审计」,或者正在挑注入检测方案、需要一套不被厂商榜单牵着走的验证办法。
不适用:纯只读、纯对话的使用 —— 那类用允许清单加记录就够,硬加审计点只会造成审批疲劳,而疲劳本身就是安全问题。也不适用:把「注意安全」写进系统提示词 —— 那不是门,那是建议。
自测 · 5 问
我说得出「它读到的哪一类内容最可能带指令」吗 —— 网页、issue、别人的工具返回,还是别的?
我的检查点是在入口 / 在跑完,还是在「要动的那一刻」?前者两个都够不着。
拦一条动作时,我判的是「像不像攻击」,还是「有没有一条可指认的、来自攻击者可触及处的、未被授权的实质影响」?
如果有多个 agent 串起来,我有没有问过一句:上游当数据读完的东西,下游会不会当指令接住?
我的检测手段,是用我自己环境里的真实工具输出验过的,还是只看过它的公开分数?
怎么上手 · 验证步骤
照这个顺序上手,别一次全铺。
第一步:先只圈一个动作。挑一个破坏性、且你以后想让它无人值守跑的动作(比如「删除一批记录」),只给这一个加检查点,别的一动不动 —— 一次全上会立刻变成审批疲劳,而疲劳会让人把门关掉。第二步:把这个动作写清楚。拆成四样并写下来:对哪个资源、做哪个动作、哪个参数最关键、这个参数正常应该从哪来。写不出「从哪来」,说明这一步还没准备好加门。第三步:拿三个问题过一遍真实轨迹。翻一次已经跑过的任务,把它临到动作前的那一步拎出来,逐条问三句;三个都成立的记成「本应拦住」,只成立一两个的记成「不拦,但要留痕」。第四步:验证。在你自己的环境里制造一次注入(往它要读的内容里塞一句让它改动作的话),看你的门是在动作发生前拦下、还是在事后才被发现;同时记两个数 —— 真拦住的比例与误拦的比例。⛔ 别只看召回:误拦高的门,人两天就学会了无脑放行,那时它等于不存在。