ARTICLE · 1145204
检索回来的文档,为什么比用户输入更危险
前面几期分别讲了模型怎么被别人的文字骗到,以及攻击者怎么往你的知识库里塞文档。这两件事都发生在同一个架构上。这一期不再从单个入口切进去,把 RAG 的攻击面完整铺一遍,再把防御拆成五层,每一层交给谁、今天能做什么,都写清楚。
如果你第一次看这个系列,只需要记住一句话就够了:RAG 把用户输入不可信这条原则守得很严,却把检索回来的文档当成自己人。而这两段文字,最后进的是同一个上下文窗口。
这句话不是修辞。它决定了后面每一类攻击为什么成立,也决定了防御为什么必须分层而不能只加一个过滤器。
本 文 速 览
01 一个数字,一个漏洞
02 信任边界:三段管道,三个错误假设
03 攻击面一:语料投毒,不需要很多只需要很准
04 攻击面二:检索层的劫持与阻断
05 攻击面三:向量库——权限、缓存与可反演的向量
06 攻击面四:间接注入,与被回写放大的污染
07 防御:五层纵深,从能做的开始
08 两个时间点,两组检测信号
先把两件已经公开的事摆出来,后面的判断都建立在它们之上。
- RAG-Poison 基准测试
(华盛顿大学与苏黎世联邦理工学院,2025 年 10 月):在 LangChain + Pinecone、LlamaIndex + Weaviate、Azure AI Search + GPT-5 三套商业栈上,攻击者控制的文档覆盖系统指令的成功率是 34% 到 71%,取决于检索配置。负载本身只是一句短话,装成正常技术文档的样子。 - EchoLeak(CVE-2025-32711)
:Microsoft 365 Copilot 上的间接提示注入。一封精心构造的邮件就够,用户 不需要点击任何链接。指令藏在将来会被检索到的内容里,绕过产品的跨提示注入分类器,再用自动加载的图片把数据带出去。
把这两件事叠在一起,有一个细节值得单独拎出来记住:payload 在被检索到之前是惰性的。它在语料库里躺着的时候什么都不做,只有被检索、被拼进上下文的那一刻才开始生效。
所以入库时扫描和查询时扫描,不是同一个动作的两次执行,而是两种覆盖范围不同的控制。前者管的是“已知的坏文档”,后者管的是“这一刻被取回来的这几段”。只做前者的团队,漏掉的正是后者的全部场景。

RAG 的管道可以粗分成三段:入库、检索、生成。每一段都建立在一个看起来合理的假设上,而这三个假设各自都是错的。
- 入库阶段假设:内部文档是可信的。
实际上一份文档只要能被修改,无论修改者是谁,就等于一条写入指令的通道。ConfusedPilot 的研究给出的门槛极低:能往被索引的目录里放一个文件就够了,攻击者甚至不需要接触 RAG 系统本身。 - 检索阶段假设:相似度高就是安全的。
相似度是一个数学性质,不是一个安全性质。它衡量的是两段文字在向量空间里有多近,完全不衡量这段文字可不可信、该不该被采纳。 - 生成阶段假设:模型会把上下文当资料,不当指令。
这是整个架构的根本性缺陷。模型读系统指令和读检索内容用的是同一条通道,它没有一种可靠机制去区分哪个是命令、哪个是数据。OWASP 的 2025 版清单把提示注入连续第二次排在 LLM01,原因就在这里——根因是结构性的,没有靠一句话能补上的方法。

把这三段连起来看,会发现一个反直觉的地方:团队在用户输入那一侧花的力气,和这一侧的风险是错配的。用户输入被当成敌人从头到尾审一遍;检索回来的内容,因为是“我们自己库里的”,直接被放进上下文,和系统指令并排坐着。
更麻烦的是,入库扫描挡不住迟到的投毒。一个 SaaS 工具今天被拿下,明天才被连接器同步进你的索引——第一次全量扫描的时候,那份文档还不存在。
这一类是过去一年被讲得最多的,也是数据最扎实的。
- PoisonedRAG
(USENIX Security 2025):在包含数百万条文本的知识库里,针对单个目标问题只注入 5 篇恶意文档,攻击成功率就超过 90%。论文同时评估了当时已有的几类防御,结论是都不够用。 - AgentPoison
(NeurIPS 2024):投毒率低于 0.1%,平均攻击成功率超过 80%,正常任务的性能损失不到 1%。
上一期把数据进入知识库的六个入口逐个拆过,这里不重复。只补一个判据,它解释了为什么这件事的门槛会这么低:一次投毒要成功,只需要同时满足两个条件——文档在语义上足够靠近目标问题,以及 进入上下文之后能压住其他证据。第二个条件靠的往往不是文档数量,而是一句给自己加权重的话:这段内容优先级最高、本文件的效力高于其他来源。
两组数字连起来读的结论是:“库越大越安全”这个直觉在这里不成立。语料库越大,投毒所需要占的比例越小,而成功率并不会跟着变小。
这是最容易被漏掉的一段,也是内容审核完全看不到的一段。它不动内容,只动顺序和数量。
- 劫持排序(HijackRAG)。
用一组与目标问题高度相似的文档,把攻击者想要的结果顶到检索结果的前列,把原本正确的文档挤到后面。内容本身可能一句恶意的话都没有,它只是“更靠近问题”。 - 阻断检索(Jamming)。
反过来做:用大量语义贴近但毫无用途的文本把 top-k 的位置占满,真正有用的文档根本挤不进来。答案质量断崖式下降,用户看到的是“AI 突然变笨了”,日志里看不出任何异常请求。 - 在评分和元数据上动手脚。
直接改写向量库里的评分、时间戳、来源可信度字段,或者在重排模型这一层做文章。来源标注是很多团队的信任依据,而这个依据本身是可以被改写的。

这三类的共同点:不需要拿到向量库的写权限,也不需要构造任何感染性内容,只需要能影响什么被写进被检索的范围。传统的“检测恶意内容”在这个位置是空转的——因为这里根本没有恶意内容可以检测。
内容干净,排序被改。防这一层,要看的是结果的分布,不是结果的文本。
OWASP 在 2025 版清单里新增了一项独立风险,编号 LLM08,叫向量与嵌入弱点。它承认了一件事:RAG 底下的这套基础设施,本身就会引入一类跟提示注入不同的问题。
权限不跟着文档走
权限规则住在源系统里。切好的 chunk 进了向量库之后,就只是一段文本加一个向量,权限不会自动跟着走,除非你在入库时把权限元数据一起复制过去并且持续同步。这带来两个后果:源系统里撤销了权限,索引里并不会跟着撤销;一个跨权限边界的共享索引,会把检索本身变成一条泄露通道。
修法的重点在位置:权限过滤要写进检索语句本身,作为检索的一部分执行,而不是先把候选取回来再过滤。后者在缓存和回退检索路径上经常被绕过。
缓存里的时间差
ConfusedPilot 记录了另一种形态:检索缓存会把已经删除、或者权限已经收回的文档,继续交给低权限用户。研究者称它为瞬时访问控制失效。这是一个只在缓存层才能被看到的窗口,源系统的审计日志里干干净净。
嵌入不是脱敏
ACL 2024 的迁移式嵌入反演研究表明,拿到向量之后,可以恢复出 50% 到 70% 的原始词语。 EMNLP 2023 的工作更进一步:对 32 个词的短输入,92% 可以精确还原,研究还从一批临床笔记的向量里复原出了完整的姓名。 2025 年初的 ALGEN 攻击显示,只需要 1000 条样本就能训练出一个黑盒反演模型,而且能跨编码器、跨语言迁移。
一个客户文档的向量库,就是一个可还原的客户文本库。它需要和原始文档同等强度的访问控制、加密和留存策略,而不是被当成一堆“看不懂的数字”。

前面几类是在“改变模型看到什么”,这一类是直接在检索回来的内容里下命令。它利用的正是前面说的那个结构性缺陷:模型分不清资料和指令。
EchoLeak 的链路值得完整看一眼,因为它把几个环节串成了一条成品攻击链:攻击者发一封邮件,邮件内容里带着指令;这封邮件在被检索时进入上下文;指令绕过了产品的跨提示注入分类器;链接改写机制也没拦住它;最后,自动加载的图片被当成外泄通道,把数据带了出去。整个过程用户没有点任何东西。
比单次注入更难处理的是回写。ConfusedPilot 记录了一个循环:一个被污染的回答,如果被员工存进 wiki、写进工单,它就变成了一个新的、可被检索的来源。实验里观察到,即使最初的恶意文档已经被删除,被污染的回答还会继续存在一段时间。
在协作型语料库上,一次污染的寿命不等于那份文档的寿命。它等于这份错误答案被复制、被引用、被再索引的寿命。
如果检索外面还套着智能体,问题的量级还会再跳一次。单次问答里,注入能做的就是改变一段回答。而当模型读完检索内容之后还能调用工具,注入拿到的是这个智能体的全部权限:删一条记录、发一条消息、动一笔数据。OWASP 把这类风险单列为过度自主权(LLM06),根因有三个——能碰到不需要的工具、工具带着超过任务所需的权限、关键动作没有人工确认。
任何单层防御都有明确的绕过路径,所以这里给的是五层叠加。每一层只解决一段,叠起来才能把成功率压下去。

第一层 入库:审查对象不是员工,是文档
来源白名单、血缘元数据、隐藏内容与指令特征清洗,命中项进隔离区、人工复核后才放行。这里最常见的误解是把审核做成“审人”——批了某个同事的上传权限,就默认他传的东西不用看。实际上要审的是文档本身:白底白字、零宽字符、1px 字号、批注里的句子,人眼看不见,模型照读。
第二层 检索:把权限写进检索语句
这一层有两个动作不能省。一是权限过滤前置:过滤条件作为检索的一部分下发,覆盖缓存和回退检索路径,不要先取回再筛。二是结果不靠拼接:把所有文档拼成一段上下文交给模型,是投毒最省事的姿势。更稳的做法是隔离后再聚合——对每份文档独立生成答案,再看哪些结论互相印证,而不是让它们在一段文本里互相说服。
第三层 生成:给资料划出一条边界
检索内容用分隔标记包起来,与系统指令分层,这套思路叫 Spotlighting。它不保证模型一定遵守边界,但能显著降低被牵着走的比例。同一层里还要收窄工具权限:智能体能碰到的工具,应该是完成任务真正需要的那几个。
第四层 输出:让答案能点回去
回答要给出原文位置,让人能点回原文核实。这一条能挡住大部分“看起来来自权威来源”的假引用——因为审核者一点就发现,那段话根本不在被引用的文档里。涉及越界、敏感或不可逆的动作,必须有人工确认这一步。
第五层 运营:让它可复跑、可撤销
准备 50 到 100 个高频问题做基线集,每周复跑一次,与上周结果对比。答案出现没有解释的漂移,就是回去查知识库的信号。同时把索引版本化,确保单个来源可以撤销:删掉一个来源的同时,能连带清掉由它派生出来的内容和缓存。
五层里最容易被跳过的是第五层。它不产生即时的安全收益,但只有它能在“已经出过问题”之后,把影响范围说清楚、把污染链条断干净。
信号分两组,对应两个不同的时间点。第一组在入库时看,第二组在查询时看。两组的覆盖面不重叠,缺任何一组都会留一个口子。

入库时看的是文档的形态:祈使句式(ignore、disregard、from now on)、提到系统与指令这类元词汇、模仿提示边界的标记(###、方括号指令、XML 标签)、文档中途切换语言、正文里出现 base64 块,以及各种不可见元素。
查询时看的是结果的分布:同一答案的几份证据互相矛盾、某段文本对最终答案的注意力占比异常偏高、同一个来源反复命中高价值问题、答案引用指向的是可自由写入的源。
最常见的缺口只有一个:内容审核只扫用户输入,不扫检索回来的段落。补上这一条的成本,比想象中低得多——每次只需要重新打分 5 到 20 个 chunk,不是整个语料库。
- 画一张信任边界图。
把能往知识库写数据的路径列全,再把能往里读的入口列全,看这两张名单有没有重叠。重叠的位置就是投毒入口——攻击者只要能写进你索引的那份文件,就同时拿到了读的权限。 - 把权限过滤挪进检索语句。
检查一遍现在是怎么做的:如果代码里是先取回 top-k 再在应用层过滤,改成把过滤条件下推到检索本身,并确认缓存和回退路径也走同一套条件。 - 建一份基线问题集。
50 到 100 个高频问题,每周复跑。同时给索引打上版本,确保删掉一个来源之后,能连带清掉它派生出来的内容和缓存。
用户输入是门外的人,检索回来的文档是你请进屋的人。门锁得再紧,也挡不住坐在客厅里的那位。
领取方式:在本号对话里回复 RAG 防御,可以领到一份《RAG 攻击面排查表》,五类攻击各自的原理、检测信号与拦截动作各一张表,可以对着自己的系统逐项过。回复 自查清单 或 30,领 30 条 LLM 应用安全检查表。
下一期把智能体这一段单独展开:工具调用的权限边界怎么划、什么动作必须人工确认、以及一个智能体被注入之后,日志里能看出什么。
来源与依据:RAG-Poison 基准测试(University of Washington 与 ETH Zürich,2025 年 10 月公开结果)、EchoLeak(CVE-2025-32711,Microsoft 365 Copilot 间接提示注入)、MSRC 与 Aim Security 的公开分析、PoisonedRAG(USENIX Security 2025)、AgentPoison(NeurIPS 2024)、HijackRAG(2024)、Jamming 类检索阻断研究(2025)、ConfusedPilot(University of Texas at Austin 预印本)、嵌入反演相关工作(EMNLP 2023、ACL 2024、ALGEN 2025)、OWASP Top 10 for LLM Applications 2025 的 LLM01、LLM06、LLM08。数据与结论以公开论文原文为准。文中提到的方法与防护措施,请仅用于你自有或已获得书面授权的系统。