在与基于 Transformer 的模型 (如 大语言模型 (LLM) 和 视觉语言模型 (VLM)) 交互时,输入结构会塑造模型的输出。但提示通常不仅仅是简单的用户查询。在实践中,它们通过动态组合来自系统指令、上下文数据和用户输入等各种来源的数据来优化响应。
在多租户环境中,多个用户共享同一应用程序基础设施,这种动态提示结构可能会带来意外的安全风险。其中一个风险来自 prefix caching 优化,如果处理不当,可能会跨用户边界泄露信息。
本文将探讨提示结构与缓存的交集,以及它们的交互如何在 LLM 驱动的应用中造成细微漏洞。通过了解这些机制,开发者可以设计出更安全的系统。
如何组合应用程序提示
如果您仅以聊天机器人用户的身份与 LLM 进行交互,您可能会将提示视为如下所示:
Build me a vacation itinerary for Orlando in August.
但在大多数真实应用中,此用户查询只是更大规模的动态构建输入(即应用提示)的 一部分。此提示通常包括多个组件,旨在更有效地塑造模型的响应。
例如,同一假期行程请求可能会转换为如下内容:
You are a helpful travel assistant. Be courteous and avoid any topics that aren’t related to travel and building itineraries. Here’s the user’s request:
Build me a vacation itinerary for Orlando in August.
Today’s date is March 1st, 2025.
The following events are happening in Orlando in August:
Marathon (August 1)
Rock Concert (August 10)
Silent Disco (August 14)
在代码中,这可能如下所示:
application_prompt = f”{system_prompt}\n{user_prompt}\n{date}\n{context}”.
在后台,应用程序获取当前日期和相关的本地事件,然后将它们拼接成语言模型的单个提示,如图 1 所示。

在本示例中,prompt 组件使用换行符 (\n
) 连接,但许多生产应用使用 <system>
、<user>
、<context>
等显式标签来分隔不同部分。无论您使用的是简单的连接还是结构化标签,其基本原理都是相同的:应用组装为 LLM 量身定制的完整提示词。
有时,应用程序 (尤其是推理和规划模型) 会在生成最终响应之前进行多次 LLM 调用。每个中间提示都可以使用先前的推理步骤、历史背景、工具输出或自动生成的子提示来动态构建。就本文而言,我们重点关注发送给 LLM 进行推理的任何字符串,而不是明确的用户看到的内容。其中包括多步推理过程中的内部步骤,即模型在后台通过几个提示建立答案。
用户在提示中可以控制或影响的数据量取决于应用程序的架构方式。在之前的假期规划示例中,用户可能只影响用户提示:“为我构建 8 月奥兰多的假期行程”。但在其他设置中,例如检索增强生成 (RAG) 应用,用户还可能影响检索并作为上下文包含的文档,从而间接控制提示的其他部分。
为什么 KV 缓存速度快
前缀缓存 是 LLM 服务系统中使用的强大性能优化。其工作原理是将模型的内部状态重复用于重复的提示前缀,使系统能够跳过冗余计算并返回更快的响应。
这实际上是使用 键值 (KV) 缓存 实现的。当模型处理输入令牌 (token) 时,它会生成代表模型状态的中间张量 (键和值) 。当新提示与前一个提示共享前缀时,系统可以重复使用这些缓存的 KV,而无需重新计算。
例如,如果 model 已处理:
The quick brown fox jumps over the lazy dog.
接着会出现一个新的提示:
The quick brown fox crosses the stream,
系统可以跳过重新计算共享前缀 “The quick brown fox”
,直接从 “crosses”
开始生成。当提示共享固定的系统指令时,这种优化尤其有效,例如旅行助手示例中的系统提示:
You are a helpful travel assistant. Be courteous and avoid any topics that aren’t related to travel and building itineraries. Here’s the user’s request:
由于每个查询都以相同的 30 个 token 开始,因此可以在所有用户请求中重复使用该前缀的模型 KV 缓存,从而大幅降低延迟和成本。在实践中,这种缓存是在块级别完成的,但为了说明目的,我们在这里进行了简化。
这种共享效率伴随着多租户环境中的权衡:prefixes 可能会在无意中成为 timing side-channel——一个用户推断其他用户提示细节的微妙方式。
Prefix 缓存信息泄露
前缀缓存通过重复使用之前计算的内部状态来缩短 LLM 响应时间。在多个用户共享同一缓存的多租户环境中,这种优化可能会引入基于时间的信息泄露。
如果两个提示共用一个长前缀,模型可以跳过重新计算这些初始共享令牌,从而加快第二个请求的速度。通过精心制作输入和测量响应延迟,攻击者可能会推断其提示的初始部分是先前看到的,这可能会泄露其他用户查询的详细信息。最近的研究已经探索了这种风险,该研究展示了 KV 缓存重用如何充当秘密信号,仅基于响应时间差泄露信息。有关详细信息,请参阅 Auditing Prompt Caching in Language Model APIs 。
示例:Inferring 位置和日期
回到旅行助手示例,假设 User A 发送以下提示:
Build me a vacation itinerary for Orlando in August.
后来,攻击者用户 B 通过发出类似的查询和测量响应时间来探测系统:
- “为我制定一份 6 月亚特兰大的假期行程。”
- “为我制定 6 月的奥兰多度假行程。” (由于用户 A 为“Build me a vacation itinerary for 奥兰多”缓存了 KVs,因此速度更快。)
- “请为我安排 7 月去 Orlando 的假期行程。”
- “为我制定 8 月的奥兰多度假行程。” (由于用户 A 缓存了 KVs,因此速度更快。)
如果最终变体的返回速度明显更快,则表明由于用户 A 的请求,该确切措辞的缓存 KV 前缀已经存在。有了足够的排列组合,攻击者就可以推理原始查询的内容,即使他们从未直接看到原始查询。
风险不仅限于用户提示
风险并不仅限于用户输入的查询。许多 LLM 应用会动态地将数据附加到检索到的文档或工具输出的提示中。这些增加虽然不受用户直接控制,但仍然可以通过时间差异观察到。
例如,用户 B 发送:
Build me a vacation itinerary for Orlando in August.\nToday’s date is February 28th.
以及
Build me a vacation itinerary for Orlando in August.\nToday’s date is March 1st.
如果只有第二个查询导致缓存命中,用户 B 可以推断其他用户 何时 提交了请求,即使日期是由应用程序附加的,而不是由用户提供。用户 B 的查询随后是否会由工具连接真实日期无关紧要;该信息不会影响前缀缓存的获取。
在基于用户身份或基于角色的访问控制获取信息的应用程序环境中,这可能会泄露应用程序端敏感或特权系统级数据,而不仅仅是用户输入的数据。
缓存配置如何影响可利用性
攻击者可以通过将此时序侧信道与有关如何配置缓存的知识相结合,进一步利用该时序侧信道。每个系统都必须决定为 KV 缓存分配多少存储空间,而且大多数实现都使用 Least Recently Used (LRU) 驱逐策略。缓存不会在固定时间内保留条目,而是会保留一个固定大小的缓冲区,仅保留最近访问的条目。随着新条目的添加,使用较少的旧条目将被逐出。
此配置为攻击者控制和测量系统状态提供了额外的上下文。例如,通过探测哪些输入仍会导致缓存命中,攻击者可能会推断出给定提示 (或前缀) 的使用时间或访问频率。这可以被动完成,也可以通过主动操作来启动或刷新缓存。
从计时测量中提取有意义的信号并非易事。LLM 系统的实际性能受多个可变性来源的影响,包括:
- 网络延迟:可能会引入与缓存行为无关的噪声。
- 批处理:将多个请求分组在一起,以更大限度地提高吞吐量。批处理引入了不确定性,因为单个请求可能会在等待批量填充时延迟,从而模糊细微的延迟差异。
- 工具和插件交互,在提示处理期间可能会调用外部 API 调用。这些调用的响应时间可能会因系统负载、数据复杂性或网络条件而异。
所有这些因素都使得很难确切地确定响应是否受益于 cache hit。不过,在合适的条件下,尤其是在较短、无需工具的提示或低流量环境中,timing signals 可能足够强大,足以推理出有用的信息。
设计更安全的系统
前缀缓存带来的风险直接产生于在生产环境中构建和使用 prompts 的方式。幸运的是,developers 可以在不牺牲性能的情况下采取切实可行的措施来降低曝光率。
提示结构很重要
最有效的方法之一是有意识地确定提示的组装方式。通过串联动态生成应用程序提示时,请考虑使用以下顺序来更大限度地降低风险:
- 系统提示
- 唯一用户或会话标识符
- 来自工具、插件或数据存储的 Augmentation context
- 用户提示 (sanitized 和 validated)
结构化提示以减少跨用户碰撞
前缀缓存的工作原理是识别共享前缀,因此用户提示之间的重叠越多,泄露的风险就越大。一种简单的缓解措施是通过在提示词的早期添加不可猜测的用户特定标识符来分解用户之间的常见前缀。
例如:
<system>
You are a helpful travel assistant.
<session> Session-ID: 2f3e1a...
<context> ...
<user> Build me a vacation itinerary for Orlando in August.
这不一定是一个 secret token,但它应该是匿名的 (无法识别个人身份) ,难以猜测或难以强制,并定期旋转。在提示词的早期添加此功能会强制用户之间进行缓存分离,从而显著降低信息泄露的几率。
限制和验证用户控制的输入
开发者还应注意验证和清理用户输入,然后再将其整合到提示中。这在基于用户查询获取文档、日期或上下文的 RAG 系统或应用中尤其如此。如果可能:
- 设置最大长度以防止溢出攻击。
- 在用户输入之前放置工具增强上下文,以避免其被挤出模型的上下文窗口。
- 避免在多个提示组件中不必要地重复用户输入。
这些技术是更广泛的提示强化策略的一部分。如果您希望获得这些类型的对抗行为和防御的实践经验,请查看 探索对抗机器学习 NVIDIA 深度学习培训中心课程。
考虑 cache 分区或隔离
如果基础设施允许,请考虑在租户之间完全隔离 KV 缓存。这可能意味着:
- 按会话或租户 ID 进行分区
- 每个租户使用不同的 cache keys
- 在高风险环境中禁用 Prefix caching
虽然这可能会降低一些性能优势,但在受监管或敏感的环境中,这可能是一个值得权衡的问题。
用于基于 Timing 的枚举的 Monitor
即使采取了缓解措施,也应监控系统是否存在可疑模式,这些模式表明存在列举尝试,例如细微变化的重复查询、大量近乎重复的提示或延迟敏感探测。将速率限制与异常检测配对有助于标记潜在的滥用情况。
总结
前缀缓存重用可为 LLM 应用带来显著的性能提升,但在共享环境中,这也会带来细微的信息泄露风险。当从用户输入、工具输出和上下文数据中动态组合提示时,该提示的结构是影响性能和安全性的设计选择。
共享缓存的缓存重用可能会让坚定的攻击者通过时间侧信道推断其他用户的部分提示。当应用程序包含受用户影响的敏感上下文或依赖身份的上下文时,这种披露的影响就会放大。
通过隔离缓存使用情况、仔细构建提示词并仔细验证输入,开发者可以在保持性能的同时降低风险。如需亲身体验这些威胁和相关威胁,请 探索 NVIDIA 深度学习培训中心 (Deep Learning Institute) 课程 。要更深入地了解 AI 系统在现实世界中的红色团队见解和技术,请查看 相关的 NVIDIA 技术博客文章 。