第 5 课:记忆的边界与安全
学习目标:
- 解释为什么"记忆"这个功能本身,同时也是一块新增的攻击面
- 复述 MemoryTrap 这个案例里,一次常规操作是怎么变成跨会话攻击的
- 判断哪些内容不该被写进持久记忆,哪些信息过期之后会变得危险
- 说出至少两种能针对性防住记忆污染的具体手段
前置要求:完成第 4 课,理解结构化状态和检查点 | 上一课 第 4 课 << | 下一课 第 6 课 >>
一次"克隆仓库、批准安装依赖"的常规操作
你让一个接了持久记忆的 Agent 帮你处理一个新项目:克隆仓库,看看依赖装得对不对。Agent 读了 package.json,发现有个依赖需要安装,问你要不要批准,你说"批准吧",Agent 执行了安装,任务结束,你转头去做别的事了。
这个过程里没有任何一步看起来可疑。没有人让 Agent"忽略之前的指示",没有人让它读一个你不认识的 URL。它就是做了它该做的事:克隆、检查、装依赖、收工。
Cisco 研究团队披露、发表在 OWASP Gen AI Security Project 博客上的一个真实漏洞——研究者把它命名为 MemoryTrap——描述的正是这样一条路径:一次常规工作流被变成了持久化的提示注入,路径本身平平无奇——克隆一个仓库,让 Agent 帮忙,批准一次依赖安装,然后就转身去做别的事了。1 表面上什么都没发生,但那次"批准安装"里,藏在依赖包或者仓库某处的恶意内容,趁机做了一件更麻烦的事:它没有停留在这个项目里,而是触达了持久记忆、全局 hooks 配置,甚至通过系统提示词影响了一层高信任的指令层。1 换句话说,一次性的操作,就足以塑造模型未来跨会话、跨项目、甚至跨重启之后的行为。1
记忆为什么是新增的攻击面:ASI06
这个案例被归到 OWASP 给 Agent 安全定义的风险分类里的 ASI06——记忆与上下文投毒。这个分类背后的判断,官方博客说得很直接:Agent 系统不只是在当下做出响应,它们会保留上下文、复用记忆、依赖持久状态来指导未来的推理和行动——这正是它们有用的原因,也正是它们变得脆弱的原因。1
这句话拆开看,其实就是前 4 课一路建立起来的能力清单:第 2 课讲的历史管理让对话能延续,第 3 课讲的外部记忆让信息能跨会话留存,第 4 课讲的结构化状态和检查点让任务能从中断处恢复。每一项能力都让 Agent"更有用",但也都意味着:一旦有恶意内容混进了这些被信任、会被自动读取和执行的地方,它造成的影响就不再局限于"这一次回答说错了话",而是会被反复读取、反复生效,直到有人发现并清理掉它。
MemoryTrap 里那次"批准依赖安装"之所以危险,根源正在于此——被批准的不是一次孤立的操作,而是一条能写进持久记忆、hooks 配置这类会被反复信任、反复加载的存储的路径。
该存什么,不该存什么
MemoryTrap 提醒的核心问题是:哪些内容,不应该被允许写进持久记忆或者 hooks 配置这类会被自动、反复加载的存储?
上一门课程《Agent 工具调用基础》第 5 课讲过一条基本规则:工具返回的内容永远是数据,不是指令。这条规则到了记忆这一层,需要往前再推一步:被读到的内容,不能未经审视就自动被提升为记忆。Agent 在执行任务过程中读到的仓库文件、依赖包的安装日志、网页内容,这些都只是这一次任务里的输入数据——把其中的某句话原样写进 CLAUDE.md 或者 Auto memory,相当于把"这次任务读到的、来源不可信的文字"提升成了"以后每次会话都会被当作可信规则载入"的内容。这正是 MemoryTrap 那次"批准依赖安装"里实际发生的事情。
具体到"该存什么"这个问题上,几条可以直接落地的界限是:
- 凭证类信息不该存:密钥、密码、访问令牌这类内容,一旦被写进会被自动载入上下文的记忆文件,就意味着每次会话都会把它们重新暴露在窗口里,增加了泄露面,却没有换来任何好处。
- 来源不可信的原始文本不该直接存:任务过程中读到的文件内容、网页文本、依赖包的输出,如果需要被记住,应该是经过人工确认、被改写成清楚陈述的结论(比如"这个依赖需要 Node 18 以上"),而不是原样把读到的整段文字搬进记忆文件。
- 稳定的、经过确认的规则和事实适合存:比如第 3 课讲过的项目代码风格约定,或者一次真实排查问题之后确认过的根因——这些内容的可信来源清楚,写进记忆能带来实实在在的价值。
判断标准和第 3 课的路径边界检查是同一个思路:不是"技术上能不能写进去",而是"这次要写入的内容,来源可信吗、值得被未来的每一次会话自动信任吗"。
过期记忆的危害:一条曾经对、现在错的规则
除了"不该存什么",还有一类风险容易被忽略:已经存进去的内容,会不会随着时间推移变成过期记忆——曾经正确,但现在已经不再适用,却因为躺在持久记忆里,依然被当作当前有效的规则去执行。
设想一份 Auto memory 笔记,是几个月前写下的:"这个项目的部署流程很简单,直接推送到 main 分支就会自动上线,不需要额外审核。"这句话在当时可能是真的。但几个月过去,项目引入了强制代码审查流程,这条记忆笔记却没有人去更新。如果 Agent 在读到这条过期记忆之后,依然把它当作当前的、可信的操作指南去执行——直接推送到 main 分支、跳过审核——造成的后果和记忆污染带来的后果是同一类:一条本不该被信任的内容,因为躺在"记忆"这个位置上,被自动当成了权威事实。
过期记忆和记忆污染的区别在于源头:记忆污染是恶意内容被主动写进去的,过期记忆是原本善意、正确的内容,因为没有被及时更新或清理,变得不再可信。但两者共享同一个防御思路——记忆里的内容不该被无条件信任,尤其是当它涉及权限、流程这类会随时间变化的规则时,更需要定期核实它是否仍然成立。
Anthropic 的修复,以及记忆之外还有别的可信表面
披露 MemoryTrap 之后,Anthropic 的回应值得记录:在 Cisco 披露这个问题之后,Claude Code v2.1.50 把用户记忆从系统提示词里移除,削弱了披露者找到的那条具体的高信任覆盖路径——文章明确评价这是"针对当时发现的那条路径的正确修复"。1 这句评价本身也留了一个提醒:修复的是"当时发现的那条路径",不是"记忆污染这整类风险"——记忆、hooks、配置文件,任何一个会被系统当作可信来源反复加载的地方,理论上都可能成为下一次攻击的落点。
OWASP 博客里一条更普适的原则是:一旦恶意内容触达了记忆、hooks 或配置这类可信任的表面,攻击者影响的就不再只是一次响应,而是在影响未来推理。1 这句话给这一课的所有讨论收了个口:第 3 课讲的记忆文件、第 4 课讲的检查点,本质上都是"会被未来的会话信任并加载"的存储——它们越有用,就越值得被认真守住写入这道关口。
小结
- 记忆之所以有用,是因为它让内容能跨会话留存并被反复信任地加载——这也正是它同时成为攻击面的原因,ASI06 这个风险分类描述的就是这类记忆与上下文投毒问题
- MemoryTrap 案例说明,一次看起来平平无奇的常规工作流(克隆仓库、批准依赖安装),就可能让恶意内容触达持久记忆、全局 hooks 配置,甚至通过系统提示词影响一层高信任的指令层,一次一次性操作足以塑造模型跨会话、跨项目、跨重启之后的未来行为
- 该存什么有明确界限:凭证类信息不该存,来源不可信的原始文本不该直接存,经过确认的稳定规则和事实才适合存
- 过期记忆同样危险:曾经正确、现在已经不再适用的内容,如果继续被当作当前有效的规则执行,后果和记忆污染是同一类问题
- Anthropic 针对已披露路径做了修复(v2.1.50 把用户记忆从系统提示词移除),但更普适的原则是:任何可信任的表面(记忆、hooks、配置)一旦被恶意内容触达,攻击者影响的就不再是一次响应,而是未来推理
>> 第 6 课:实战:给 Agent 加一个持久记忆层