文章总结: 本文探讨大模型API的PromptCaching缓存机制,包括其工作原理、缓存命中与未命中的区别,以及从请求侧提升命中率的策略,如按稳定性梯度组织提示词、规范化序列化等。文章还介绍了KVCache时间侧信道攻击,攻击者可通过观察缓存命中与否推断用户输入前缀,属于安全风险。可操作建议包括优化提示词结构、实施粘性路由,并注意缓存隔离。 综合评分: 86 文章分类: AI安全,漏洞分析,威胁情报,安全工具,安全建设
大模型探索系列:LLM缓存优化与KV Cache时间侧信道攻击
宁宇飞 宁宇飞
国家网络空间安全云社区
2026年7月27日 10:05 上海
在小说阅读器读本章
去阅读
现在做Agent工程基本都离不开LLM API,调用产生的开销是实打实要把控的硬成本,其中以缓存命中率尤为关键。
长期使用各类大模型接口的同行都能发现,同一段对话,缓存命中和未命中的花费差距巨大。我们判断服务商好不好,缓存效率与计费规则是很重要的参考标准。不少人觉得缓存策略完全由平台掌控,使用者无法优化,实则不然。
本文是“大模型探索系列”第一篇,分享大模型缓存的由来、如何自主提升缓存命中,还有可能存在的KV Cache时间侧信道攻击。
Prompt Caching 如何工作
Prompt Caching(提示词缓存,也称前缀缓存)用于复用多个请求之间相同的提示词前缀,从而减少重复的输入计算、降低首token延迟,并在支持的API中降低输入成本。
它缓存的不是模型生成的文本答案,也不是向量数据库式的语义检索结果。命中通常要求提示词从开头起具有完全相同的token前缀。
➢推理的两个阶段:Prefill 与 Decode
典型的大语言模型推理包含两个主要阶段:
Prefill(输入预填充) 模型处理全部输入token,计算每一层的中间表示以及注意力所需的Key/Value状态。
Decode(逐token生成) 模型逐步生成输出token,并复用此前已经计算的注意力状态。
一句话来说就是输入Prompt,到Prefill(处理输入并建立注意力状态),再Decode(逐 token 生成输出)。
如下图:
对于标准因果Transformer,在某一层中,位置 iii的状态受到此前token的影响。因此,只有当请求拥有完全相同的前缀时,之前计算的注意力状态才可以安全复用。
我们可以假设一个现象。假设应用在100个请求中重复发送相同的10,000-token系统指令或参考文档。如果没有跨请求缓存,服务端可能需要对这10,000个token重复执行100次输入计算。Prompt Caching的目的,就是避免重复计算相同前缀。
➢实际缓存的是什么
深入来说需要区分两种缓存:
请求内KV Cache
这是标准自回归生成中的缓存机制。在生成同一个响应时,模型保留已经处理过的token的注意力状态,避免每生成一个新token都重新处理全部历史内容。
跨请求Prompt Cache
跨请求缓存会保留可复用提示词前缀的模型中间状态。对于标准 Transformer,这通常表现为各注意力层的 Key/Value 张量,即 KV Cache。
这些状态可能保存在:
- GPU 显存
- 主机内存
- GPU 本地高速存储;
- 分布式或分层缓存系统。
具体存储方式属于模型服务商的实现细节,我们不能假定所有API都使用完全相同的缓存结构。
感兴趣想要深入了解这部分内容的读者可以参考OpenAI Prompt Caching文档。文档明确说明,其部分扩展保留策略会将KV张量卸载到GPU本地存储,而且不会把原始提示词文本写入该存储层。
➢接下来就是我们关注的缓存命中
缓存命令的一般过程存在一个逻辑门。
先收到API请求,第二步分词并生成前缀标识,然后查找最长的可复用前缀,这里开始出现分支。如果匹配到,就会读取缓存状态,然后只计算新增后缀最终生成输出。如果没有匹配到,就要计算完整的输入,并写入可缓存前缀,才会生成输出,给予我们响应内容。
如下图:
接下来详细拆解一下这个过程。
第一步:确定缓存键
系统需要为提示词前缀建立可比较的标识。实际实现可能使用:
- token 块哈希
- 递归或累计哈希
- 前缀树
- 路由键与前缀哈希的组合
- 模型和请求配置等附加元数据
因此,下面的公式适合作为概念模型,但不应被描述为所有API的实际实现:
Hm=Hash(t0,t1,…,tm,metadata)
其中,metadata可能包括模型版本、工具定义、图片处理参数或缓存命名空间等信息。以OpenAI为例,请求会依据初始前缀的哈希进行路由;若提供prompt_cache_key,它也会参与路由和匹配。
第二步:精确前缀匹配
Prompt Caching的关键规则是:缓存命中依赖相同的token前缀,而不是语义相似。
如果两个请求的前1,000个token完全相同,这段前缀就可能被复用。
如果某处文本发生变化,分词结果可能从第一个不同token开始改变。该位置之后的更长前缀通常不能继续命中,但变化位置之前已经存在的较短缓存块仍可能复用。
例如:
Bash
请求 A:固定系统指令 + 文档版本 1 + 用户问题
请求 B:固定系统指令 + 文档版本 2 + 用户问题
在这个例子中,即使文档不同,二者仍可能命中前面的固定系统指令;并不一定是“整个缓存全部失效”。
第三步:复用前缀并计算后缀
这里就会出现一个Cache miss的现象,它其实就是指当用户的提示词或请求无法在系统内存中找到时,就会发生LLM缓存未命中(cache miss),迫使LLM完全从头开始处理输入。与“缓存命中”(cache hit)相比,这种情况会导致更高的计算成本、更慢的响应速度以及更多的 API Token消耗。
那么到了逻辑门可能出现的两个分支,决定了本次请求的成本。
- Cache miss:处理完整输入,并可能把合格的前缀写入缓存。
- Cache hit:复用已缓存前缀的中间状态,仅对未缓存的后缀执行必要的输入计算。
到了这一步,除了大幅影响Token的成本以外,还有一个开头提到了一个关键数据,如果发生了Cache Hit,此时不仅按较低的缓存读取费率计费,并且首字响应时间(TTFT,Time to First Token)也会大幅缩短。
➢API 提供的缓存控制方式
当前主流的 LLM API 主要提供自动缓存和显式断点两种方式,而且二者并不互斥。
自动缓存
开发者保持普通请求结构,由服务端自动识别可缓存前缀。
OpenAI对达到最低长度要求的提示词默认启用自动缓存。当前最低门槛为1,024 token;低于该门槛时,cached_tokens为0。
OpenAI建议把稳定内容放在前面,把用户相关或经常变化的内容放在末尾。图片、工具定义等内容若参与前缀,也必须保持一致。
如下面的JSON:
JSON { “model”: “gpt-5.5”, “messages”: [ { “role”: “system”, “content”: “这里是较长且稳定的系统指令……” }, { “role”: “user”, “content”: “法国的首都是哪里?” } ] }
还有一类显式缓存断点
显式断点让开发者指定“可复用前缀到哪里结束”。比如Anthropic支持在内容块上放置 cache_control:
JSON
{
“model”: “
cache_control标记表示此前的完整前缀可以作为缓存候选,而不是只缓存当前文本块。Anthropic目前同时支持顶层自动缓存和内容块级显式断点,默认TTL为5分钟,也提供收费更高的1小时TTL。此外,OpenAI当前的GPT-5.6及后续模型家族也支持显式 prompt_cache_breakpoint。
我们大致说完了缓存命中的定义,用下面一张表来概括和区分缓存命中成功与缓存失败。
从请求侧提升缓存命中
要系统性提高大语言模型API请求的提示词缓存命中率,提示词结构、工具声明、代理路由和数据序列化方式,应尽量符合供应商或自托管推理引擎的前缀缓存机制。
上面我们说过,提示词缓存通常复用模型在 Prefill 阶段为既有输入前缀计算出的KV状态。缓存能否命中,主要取决于模型最终看到的输入前缀是否与已有缓存前缀一致。如果某个较早位置的Token、内容块或影响提示词渲染的配置发生变化,则该位置之后的缓存通常无法继续复用,但变化点之前的较短稳定前缀仍可能命中。
这里需要注意的是,缓存命中并不受以下策略“保证”。首次请求、TTL到期、缓存驱逐、路由变化、最低长度不足以及供应商容量限制,都可能造成缓存未命中。
➢按“稳定性梯度”组织提示词
基本原则上面也讲过:
- 越稳定、复用频率越高的内容越靠前;
- 越动态的内容越靠后。
Bash [较稳定]
- 长期不变的系统或开发者指令
- 稳定的工具及函数 Schema
- 稳定的示例、政策或共享参考资料
- 已有对话历史
- 本次请求的动态上下文
- 当前用户问题
[较动态]
这样做可以最大化多个请求共享的连续前缀,使推理引擎能够复用更长的KV缓存,只对新增或变化的后缀执行Prefill。
这里有几个风险点:
- 不同大模型厂商可能采用固定的内部顺序。例如Anthropic的前缀层级为tools→system→ messages。
- 查询时临时检索出的RAG文档属于动态内容,不应一律放入长期静态前缀。
- 如果参考资料按天、按版本或按租户变化,可分别建立稳定版本,并在相同版本内复用缓存。
- 工具定义通常必须保留在正式的tools字段中,不能简单移入普通用户消息。
➢避免在稳定前缀中过早插入动态值
下面是一个反例:
Bash System: “You are a helpful assistant. Today is 2026-07-21. User ID is 84920.”
如果日期或用户ID位于长提示词的前部,它们的变化会使后续前缀无法复用。
比较合适的优化方案是:
SQL System: “You are a helpful assistant. Treat the trusted runtime-context block as request metadata.”
…稳定工具和参考资料…
Trusted runtime context: { “date”: “2026-07-21”, “user_id”: “84920” }
User: “What is on my schedule today?”
动态值应位于稳定前缀之后,但不能为了缓存而削弱安全边界。身份、权限和租户信息应由应用以可信结构注入,并在工具执行层再次验证,而不是完全依赖普通用户文本。
➢规范化模板和序列化过程
在Agent开发过程中,最好确保真正进入模型上下文的内容具有确定性:
- 固定提示词模板版本;
- 统一换行符、空白和Unicode规范化方式;
- 保持工具数组及工具Schema的顺序稳定;
- 保持结构化输出Schema稳定;
- 对RAG文档采用确定性的排序和分块规则;
- 避免由无序集合、随机ID或当前时间生成前缀内容;
- 对会影响内部提示词的功能开关进行版本化。
如果应用主动把JSON序列化成提示词文本,排序对象键可以提高稳定性。但对于直接提交给供应商API的外层JSON,sort_keys=True并不是普遍成立的缓存要求,因为供应商通常会先解析请求,再按自己的格式构造模型输入。数组顺序和最终渲染内容通常比外层JSON对象键顺序更重要。
➢显式控制的key
刚才上面也说到了如OpenAI Responses/Chat API的prompt_cache_key和Anthropic Claude API的cache_control断点等,这部分可以详细参考官方文档。
➢实施粘性会话代理路由
还有一类,自托管的开源权重推理引擎(例如 vLLM、SGLang,或者像 New API / Bifrost 这样的多实例代理网关):
- 一致性哈希 / 粘性路由: 把共享同一个前缀哈希(或相同租户system prompt)的请求路由到同一个worker节点。如果请求在10个 GPU 节点之间随机负载均衡,那么实际 KV cache 命中率会大幅下降,因为每个节点收到的都是冷请求。
- 前缀缓存隔离与加盐: 当服务多个租户时,配置缓存隔离盐值,让租户特定前缀不会泄漏,同时还能让共享的 system 指令命中全局缓存块。
➢非常规手段
还有一种为了达到100%缓存命中的非常规手段,就是把提供方prompt caching和应用层缓存结合起来。可以参考下表:
安全时间:KV Cache时间侧信道攻击
终于到了安全时间,让我们先来看看KV Cache 时间侧信道攻击是什么意思?简而言之,通过一种攻击方式(通常是API调用),得知共享前缀缓存是否命中,会改变请求的计算量、调度行为或首Token延迟。攻击者观察这些外部差异,就可能推断某个候选Token前缀是否曾被其他请求处理。
下面是缓存侧信道在 LLM 推理系统中的变体:
严格来说,攻击者首先得到的只是:
这个完整Token 前缀可能存在于攻击者可访问的缓存隔离域中。
➢思考:攻击者真正探测的是什么
我们要知道,前缀缓存不是全文搜索引擎,也不是“某短语是否出现过”的集合。
假设受害者的最终 Prompt 是:
Bash [系统指令] [工具 Schema] [用户历史] Project Apollo merger details
攻击者发送:
Bash Project Apollo merger details
会造成什么后果 ?缓存一般不会命中,因为两者从第一个Token起就不同。
攻击者必须尽可能复现:
Bash 相同模型
- 相同 Tokenizer
- 相同聊天模板
- 相同系统和工具前缀
- 相同前序内容
- 待验证候选内容
因此,缓存时间侧信道主要提供的是:前缀成员推断:候选前缀 (P|X) 是否已存在于共享缓存中。而不是:子串查询:短语X是否在任意用户对话的任意位置出现过。
➢基本攻击流程
阶段 1:受害者填充缓存
受害者发送:
Plain Text P || Secret || Suffix
其中:
- P 是攻击者已知或能够猜测的公共前缀;
- Secret 是未知内容;
- Suffix 是后续内容。
服务端完成Prefill后,相关前缀可能留在共享KV Cache中。
阶段 2:攻击者构造候选
攻击者准备多个候选:
Plain Text P || Candidate_1 P || Candidate_2 P || Candidate_3
如果 Candidate_2 与受害者的秘密前缀一致,则它可能比其他候选多命中一个或多个缓存块。
阶段 3:观察侧信号
攻击者可能观察:
- TTFT
- 多个并发请求的返回顺序
- 服务端报告的缓存 Token 数
- 缓存驱逐行为
- 请求是否被优先调度
阶段 4:统计分类
攻击者不能只依赖单次延迟,而会比较:
Bash [Tcandidate 与 Tknown-miss]
并使用:
- 重复采样
- 命中/未命中阈值
- 相对延迟
- 对照候选
- 假设检验或分类器
此时,如果某个候选的延迟分布显著偏低,攻击者便把它判断为可能命中。
➢主流的三类攻击
候选前缀成员推断
最简单的一类:
Plain Text 候选 A:Project Apollo was cancelled 候选 B:Project Apollo was approved 候选 C:Project Apollo was delayed
攻击者不一定能恢复完整 Prompt,但可能判断某个已知候选是否曾被处理。
适合此类攻击的目标包括:
- 从有限集合中判断某种状态;
- 判断用户是否访问过某份文档;
- 判断某个已知模板是否被使用;
- 推断已知格式中的枚举字段;
- 对固定前缀后的日期、地点、部门或状态做候选测试。
这里得到的仍是“缓存域中存在该前缀”,不是对具体受害者身份的直接证明。
逐Token Prompt重建
如果攻击者已经恢复前缀 (P),可以尝试枚举下一 Token:
Plain Text P || token_1 P || token_2 P || token_3 …
若某个候选多命中一个Token或缓存块,则可能是正确延续。随后重复:
Plain Text 已恢复:P || token_i 下一轮:P || token_i || candidate_j
理论上可以逐步重建Prompt。但其实现实中存在显著困难:
- 词表可能有数万至十几万个Token;
- 一个错误Token会导致后续所有前缀错位;
- 攻击者自己的探测会把错误候选写入缓存;
- 目标缓存可能在枚举完成前被驱逐;
- 公网延迟和队列噪声可能大于单Token差异;
- 生产系统不允许攻击者清空或控制缓存;
- 攻击者未必知道完整聊天模板和系统前缀。
调度顺序侧信道
侧信号不一定是绝对延迟。
攻击者同时提交:
Plain Text Dummy-before Candidate_1 Candidate_2 Candidate_3 Dummy-after
如果某个候选多匹配一个Token,调度器可能改变其服务顺序。攻击者观察返回顺序,就能获得“该候选是否匹配”的一位信息。
这类攻击利用的是:
Plain Text 秘密前缀 → 缓存匹配长度 → 调度优先级 → 返回顺序
而不只是:
Plain Text 秘密前缀 → Prefill 时间 → TTFT
➢攻击成立所需的关键条件
- 存在跨安全主体的缓存共享
若缓存严格按用户隔离:Victim Cache ≠ Attacker Cache
攻击者就无法通过自己的请求命中受害者的缓存。缓存共享可能分为(主流模型厂商):
- 攻击者能复现前缀
攻击者发送的提示词通常要求知道或猜中:
- 系统模板;
- Prompt 结构;
- Tool Schema;
- Tokenizer;
- 固定上下文;
- 已恢复的前序 Token。
正常来说,固定格式的业务应用比自由聊天更容易受到候选攻击。如(已知模板大幅降低搜索空间):
Bash Employee record: Name: […] Department: […] Access level: [候选值]
3. 请求进入同一缓存域或 Worker
即使逻辑上共享缓存,如果请求被路由到不同 Worker,而KV Cache没有跨节点共享,也不会命中。
反过来,以下路由方式会提高攻击可观测性:
- 会话粘性路由
- 前缀哈希路由
- 缓存感知路由
- 共享分布式 KV 存储
4. 缓存存活时间足够长
攻击者需要在以下情况发生前完成测量:
- TTL 到期;
- LRU 驱逐;
- GPU 内存压力导致淘汰;
- 模型重新部署;
- Worker 重启;
- 缓存被其他流量冲刷。
5. 侧信号足够可区分
命中的前缀越长,通常节省的Prefill计算越多,信号越强。
如果只多命中一个Token,而系统同时存在:
- 强网络抖动
- 大量并发
- 动态批处理
- 工具调用
- 长排队
此时,单Token差异可能被噪声淹没。
➢系统提示词为什么可能成为目标?
系统提示词经常具备三个特征:
- 长
- 稳定
- 在大量请求中重复
这正是前缀缓存最容易复用的内容。
攻击者可能准备一个公开模板候选集:
Bash 框架 A 默认系统提示词 框架 B 默认系统提示词 版本 C 的安全护栏 某开源 Agent 模板
然后测试哪个候选出现缓存特征。这更准确地称为:
-
系统模板指纹识别;
-
候选系统 Prompt 成员推断;
-
在较强条件下的增量 Prompt 重建。
写在最后
总结来说,缓存之于LLM API是非常关键的成本与性能指标,为了深入探索可能复现出的KV Cache侧信道攻击的原理,本文从缓存原理、命中提升到攻击原理进行了梳理,对于观测与探测代码的编写,读者可自行通过大模型生成并横向对比多厂商的LLM API,获取更多观察结论。
– END –
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:国家网络空间安全云社区 宁宇飞 宁宇飞《大模型探索系列:LLM缓存优化与KV Cache时间侧信道攻击》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论