安全
AI 安全与提示注入
提示注入无法靠过滤消除,因为语言模型在指令与数据之间没有可靠的边界。防御必须是架构层面的:限制模型在被欺骗时所能做的事情。
· 阅读约 5 分钟 · Starone 工程团队
提示注入是基于大语言模型构建的应用最具代表性的安全问题,它位居 OWASP Top 10 for LLM Applications 之首并非没有道理。攻击者把文本放到模型会读到的地方,模型就会遵循这段文本,而不是开发者的指令。
我们的立场是,应当把它看作一类尚无完整修复方案的未修补漏洞。检测有帮助,也应该使用。但真正重要的设计工作,是假定模型有时会被成功操纵,并确保这种结果被控制在有限范围内。
直接注入与间接注入
直接注入来自在应用里输入内容的那个人。他们试图覆盖系统提示词、把它套取出来,或者说服模型做出产品不允许的行为。这是大家熟悉的情形,其影响通常仅限于该用户本来就能触及的范围。
间接注入更为严重。恶意文本藏在模型替一位无辜用户处理的内容里:一张被要求总结的网页、一份由 RAG 流水线检索到的文档、智能体正在分拣的收件箱里的一封邮件、工单中的某个字段、另一个工具的输出。用户从头到尾看不到这条指令。模型看得到,并且以用户的权限采取行动。
为什么无法完全过滤
SQL 注入是靠参数化查询解决的,它让数据库在代码与数据之间有了结构性的隔离。语言模型没有对应的机制。系统提示词、用户消息和检索到的文档都是同一个上下文里的 token,模型遵循指令的倾向并不取决于指令从哪里来。分隔符和角色标记只是模型通常会遵守的约定,而不是它无法越过的边界。
输入分类器和护栏模型能够减少成功攻击的数量。但它们是概率性的,攻击者可以无限次重试:换一种说法、换一种语言、换一种编码,或者把指令拆散到多份文档里。一个能挡住大多数尝试的控制手段,对付随意的滥用是有用的,对付有动机的攻击者则不够。我们把这些过滤器当作其中一层防护,而绝不把它当作认定一个设计安全的理由。
工具的权限隔离
一次注入能造成多大的破坏,取决于模型能够调用什么。一个只能生成文本供人阅读的助手,被劫持后顶多是个麻烦。而一个能发邮件、跑查询、调用内部 API 的智能体,对于控制了它输入的人来说,就是一个远程操作员。
- 工具以最终用户的权限运行,并由后端校验,绝不使用权限宽泛的服务账号
- 每个任务只暴露它所需的最小工具集,默认使用只读版本
- 工具参数在模型之外按 schema 和业务规则进行校验
- 模型一旦读取过不可信内容,在该会话的剩余时间内就失去对高影响工具的访问权
- 在可行的情况下,由一个被隔离的模型处理不可信文本,只向拥有权限的模型返回结构化、带类型的值
封堵数据外泄路径
破坏力最大的组合是:模型能访问私有数据、会读取不可信内容,并且有某种途径把信息向外发送。三者去掉任何一个,这种攻击就失去了大部分价值。对外通道往往最容易被忽视,因为它看起来不像一个工具。
一个会渲染 Markdown 图片的聊天界面,会去请求模型输出的任何 URL,而查询字符串里可以带上模型被诱导放进去的任何内容。可点击的链接、网页抓取工具、webhook,甚至发往第三方的一次搜索查询,都能起到同样的作用。我们把渲染的图片和对外请求限制在域名白名单之内,剥离或无害化处理模型生成的带参数 URL,并让智能体的网络访问统一经过出口代理。
- URL 由模型拼出来的渲染图片和链接
- 参数由模型决定的网页抓取、搜索和 webhook 工具
- 收件人由模型指定的外发邮件或消息
- 向任何攻击者事后能读到的共享位置写入数据
把模型输出当作不可信输入
模型产生的一切内容,都应当像对待匿名用户提交的数据那样处理。如果要插入页面,就按该上下文进行编码。如果要成为数据库查询的一部分,查询就必须参数化。如果要传给 shell 或解释器,这一步就在没有凭证、没有网络的沙箱里运行。结构化输出要解析并按 schema 校验,校验失败就是一个错误,而不是可以无限次让模型重来以求修复的东西。
这一点同样适用于智能体之间。在多智能体工作流中,一个智能体的输出是另一个的输入;落在第一个智能体上的注入会沿着链条一路传播,除非每一次交接都带有类型并经过检查。
有后果的操作需要人工批准
转移资金、向第三方发送消息、删除数据或更改权限的操作,应当要求由人来确认。确认界面必须展示后端将要实际执行的真实参数,而不是模型对自己打算做什么的摘要,因为这份摘要正是由那个可能已被攻破的组件生成的。
代价是审批提示很快就会失效。如果每一步都要求确认,人们就会不看内容直接点同意。我们把审批留给不可逆或对外可见的操作,让可逆、低影响的步骤在严格的限制内自动执行。只有在输入可信或者工具不可能造成损害的情况下,完全自主的智能体才是合理的选择。
像对待其他攻击面一样测试和观测它
抗注入能力会随着每一次提示词修改和模型升级而变化,所以它需要回归测试。我们维护一套对抗性输入,其中包括嵌有指令的文档,并在 CI 中针对完整流水线运行,断言的对象是工具调用,而不仅仅是文本。在生产环境中,每一次工具调用都连同它之前的内容一起记录下来,这样一旦发生事故,就能追溯到引发它的那份文档。
