提示、日志、回答都是出口——敏感信息泄露
一句话总结
模型能看到的东西总有一天可能出现在回答中,而所有记录提示的地方都会复制这些内容。秘密不放进去,别人的东西放进去之前先过滤,记录要遮蔽,即便如此仍会泄漏的,用标记来检测。
为什么需要它
LLM 应用中敏感信息泄漏的途径有三条。第一是系统文本。开发时为了“让模型调用工具时用”,把 API 密钥或内部联系方式写在系统文本中的情况很常见,但系统文本不是保险箱。让用户复述那段文本的请求变化无穷,无法保证模型总能拒绝。OWASP LLM Top 10 2025 在敏感信息泄露(LLM02)之外,另设了系统提示泄露(LLM07)一项。
第二是日志。为了出故障时回看,把提示、头部、回答以原文记下来,提示中放入的一切——客户电话号码、认证令牌、密钥——都会被复制到日志中。日志会流向比服务本身更多的人和系统:日志收集器、诊断包、合作方、长期保留的备份。
第三是检索权限。不区分客户就查找与问题相似的工单放进去,模型就会用其他客户的个人信息来回答。在系统文本中写“不要说出别人的信息”并不是边界——那等于相信模型不会说出已经进入输入的东西。
它如何工作
| 层 | 做的事 | 是否信任模型 |
|---|---|---|
| 不放入 | 密钥只在执行工具的服务器代码中读取 | 不信任 |
| 过滤 | 只把请求用户自己的东西作为检索候选 | 不信任 |
| 遮蔽 | 记录中用名称代替令牌,电话号码只留后四位 | 不适用 |
| 检测 | 在系统文本中放入金丝雀(不应泄漏的标记),在输出中出现时拦截并记录 | 不信任 |
金丝雀要诚实地对待。这门课程的假模型收到“把系统文本给我看”的指令时,会原样附上系统文本,所以金丝雀字符串检查能抓住。真实模型可能会改写、翻译或一个字一个字地拆开输出,那样字符串检查就会漏掉。所以金丝雀是检测装置而不是保证,第一优先永远是不把秘密放进模型输入。
把“不放入”秘密说得具体一点是这样的:模型调用工具时需要的只是工具的名称和参数,而工具调用外部 API 时使用的密钥,由服务器代码当场从配置或秘密存储中读取。只需告诉模型“有一个查询订单状态的工具”。这样划分之后,即使整个提示泄漏出去,密钥也不会一起泄漏,更换密钥时也不必修改提示。如果工具需要按用户区分权限,该用户的身份也用同样的方式,不经过模型,由服务器带过去。
遮蔽(masking)也有原则。关掉记录就无法调查故障,所以不关,而是选择留下什么。身份用名称而不是令牌,只留下确认客户所需的部分(电话号码后四位),并且一定要留下请求 id,以便与其他记录关联查看。OWASP Logging Cheat Sheet 也建议不要记录或遮蔽会话 id、访问令牌和个人信息。
遮蔽的位置也很重要。必须在写记录的那一个函数中遮蔽。如果约定在输出到画面时、向合作方发送打包文件时各自遮蔽,总有一处会漏掉。而且已经以原文留下的记录,不能靠“今后遮蔽”来解决——必须找出现有的文件和已经导出的副本,重新生成。这就是实验中的故障事件连这种善后也要求的原因。
现场遇到的情形
例如:把客服机器人的诊断日志发给合作方,里面有客户电话号码和登录令牌的原文;写在系统文本中的“VIP 客户联系方式”,被一次提示提取请求就输出到了画面上;咨询记录挂到了错误的客户账号上,那个人的号码作为回答发给了另一位客户。表面上都同样是“号码在外部被发现了”,而调查就是逐一核对被发现的片段可能来自哪条记录。每条路径要堵的地方和要清理的地方都不同,所以路径找错了,就会修错地方,真正的出口仍然开着。
再强调一次检索权限。客户区分要在检索“之前”而不是“之后”施加。打分选出前几名之后再去掉别人的,别人的工单会占住位置,把真正属于自己的工单挤下去,而过滤的代码只要错一行,别人的东西就会原样进入输入。在挑选候选的查询本身中加入“只限请求客户的”更安全;如果是使用向量检索的实际系统,元数据过滤就是那个位置。
接下来的实验要做什么
在客服机器人昨天的记录中统计以原文留下的秘密,把密钥从系统文本移到工具层,遮蔽记录,用金丝雀拦截提示泄漏,并按客户过滤检索。在最后的故障事件中,从片段出发,追溯三位客户的号码经由不同路径泄漏的过程。