共享 LLM 基础设施中的跨租户数据泄露:无人测试的隔离失效
大多数多租户 LLM 产品都存在一个其工程师尚未测试过的安全漏洞。这并非理论上的漏洞 —— 而是一个实实在在的漏洞,已有记录在案的攻击向量和真实的确认案例。这个漏洞在于:现代 AI 栈中的每一层都引入了自己的隔离原语,而每一层都可能以静默的方式失效,导致一个客户的数据进入另一个客户的上下文。
这与提示词注入(prompt injection)或越狱(jailbreaking)无关。它关乎基础设施本身 —— 提示词缓存(prompt caches)、向量索引(vector indexes)、内存存储(memory stores)和微调流水线(fine-tuning pipelines) —— 以及大多数团队在未经核实的情况下就交付的“隔离”这一组织层面的虚构。
在 2024 年 4 月,Wiz 的研究员展示了在某主流 AI 即服务(AI-as-a-service)平台上实现的完整跨租户入侵。攻击链贯穿了配置错误的 Kubernetes 环境和模型文件中的 pickle 反序列化,最终让攻击者能够访问整个客户群的私有模型和数据集。研究员无需绕过身份验证即可访问其他客户的数据 —— 仅仅通过利用那些单独看起来很稳固但组合方式不正确的隔离层之间的缝隙。
那次事件只是冰山一角。更微妙的失效由于无人关注而没有登上新闻头条。
KV-Cache 时序信道
当你部署像 vLLM 这样启用了自动前缀缓存(automatic prefix caching)的 LLM 服务系统时,系统会将重复提示词前缀生成的键值张量(key-value tensors)存储在 GPU 显存中,并在缓存命中时重用它们。这在效率上是一个显著的提升 —— 缓存命中可以跳过昂贵的预填充(prefill)计算,响应速度明显变快。
而这种可测量的延迟差异也是一种攻击向量。
在 NDSS 2025 上发表的研究记录了“PROMPTPEEK”类攻击,攻击者通过分析共享服务基础设施上的缓存命中/未命中时序模式,来重建其他用户的提示词。该方法不需要特殊权限 —— 只需要能够发送请求并观察响应延迟。当攻击者的探测提示词与另一个租户缓存的前缀匹配时,在统计学上,命中与未命中的差异显著性达到 p < 10⁻⁸。通过一系列探测,攻击者可以推断出其他租户正在查询的内容。
vLLM 中存在修复方案,即 cache_salt 参数,它通过将盐值(salt value)合并到数据块哈希中,为每个租户创建独立的缓存命名空间。只有带有匹配盐值的请求才能重用缓存块。但这种保护是可选的(opt-in),且需要由应用程序强制执行。默认配置 —— 也就是大多数团队部署的配置 —— 根本不提供任何跨租户的缓存隔离。
Anthropic 的托管基础设施在 2026 年初从组织级隔离切换到了工作区级(workspace-level)缓存隔离,因为他们意识到,即使是同一组织内的内部团队也不应该共享 KV-cache 数据块。如果你运行自己的服务栈,等效的隔离需要显式的检测和实现。大多数团队尚未添加此类功能。
向量数据库中的命名空间幻象
向量数据库是与基于 RAG 的泄露关联最直接的一层,也是“隔离”与“实际隔离”之间差距最大的一层。
Pinecone 命名空间、禁用多租户模式的 Weaviate 集合、具有行级安全性的 pgvector 模式 —— 所有这些都是组织层面的边界,而非加密边界。它们通过约定运作:查询包含一个过滤器,数据库限制搜索空间。导致它们失败的原因与导致 SQL 注入的原因相同 —— 边界是由应用程序代码强制执行的,而不是由存储系统本身。
具体的失效模式因数据库而异:
Pinecone 命名空间在指定时会在索引级别正确执行。失效模式在于遗漏:开发人员在编写检索调用时忘记传递命名空间参数,导致查询扫描所有租户。在代码审查中,这看起来像是一个微小的疏忽。但在生产环境中,这意味着每一个没有命名空间的查询都会返回来自任何租户数据的向量。
具有行级安全(RLS)的 pgvector 更加健壮,因为即使应用程序代码包含错误,数据库引擎也会执行策略 —— 遗漏的 WHERE 子句会被阻止,而不是静默允许。但 PostgreSQL 的查询优化器统计信息曾泄露过本应由 RLS 阻止的行(CVE-2024-10976),这表明即使是数据库层的隔离也可能在预料之外的边界点失效。RLS 并不是万灵丹;它是一个强力的默认设置,可以缩小应用程序漏洞的影响范围。
Weaviate 在主流向量数据库中提供了最强的原生多租户模型,在集合级别为每个租户提供逻辑隔离的数据。但它需要显式的多租户模式配置(非默认配置),且隔离保证取决于在每次写入和每次查询中正确设置租户键(tenant key)。
测试缺失是共同的问题。大多数团队会验证租户可以检索自己的数据。但几乎没有人验证租户不能检索另一个租户的数据。这是两种不同的测试。
一个最小化的跨租户隔离测试如下:将一个独特的文档注入租户 A 的索引,然后尝试使用租户 B 的凭证进行检索 —— 不仅仅是过滤,而是真正的以租户 B 的身份进行身份验证。如果文档出现了,说明隔离已失效。在每次修改检索配置之前,都要在 CI 中运行此测试。
// 跨租户隔离测试示例
const tenantA = await createClient({ tenantId: 'tenant-a' });
const tenantB = await createClient({ tenantId: 'tenant-b' });
const secretDoc = "INTERNAL_ONLY_PROJECT_X_PLANS";
await tenantA.index(secretDoc);
// 关键测试:Tenant B 应该无法通过语义搜索检索到它
const results = await tenantB.search("Project X plans");
assert(!results.includes(secretDoc), "CROSS-TENANT LEAKAGE DETECTED");
微调作为跨租户放大器
共享微调基础设施引入了一种大多数平台团队尚未考虑的污染风险:一个租户的训练数据可能会影响为所有租户提供服务的基础模型。
关于训练数据投毒的研究已经证实,污染少于 0.01% 的训练样本就足以植入行为后门,且这些后门在后续的安全微调中依然存在。对 1% 的指令微调数据进行投毒,可以在特定任务类别上实现 80% 的性能下降。随着训练数据规模的扩大,所需的投毒样本数量基本保持不变——这意味着更大的训练集并不会稀释攻击。
- https://thehackernews.com/2024/04/ai-as-service-providers-vulnerable-to.html
- https://www.ndss-symposium.org/wp-content/uploads/2025-1772-paper.pdf
- https://arxiv.org/html/2508.09442v1
- https://arxiv.org/pdf/2508.08438
- https://docs.vllm.ai/en/stable/design/prefix_caching/
- https://www.giskard.ai/knowledge/cross-session-leak-when-your-ai-assistant-becomes-a-data-breach
- https://arxiv.org/html/2409.18169v5
- https://arxiv.org/pdf/2510.07192
- https://redis.io/blog/data-isolation-multi-tenant-saas/
- https://medium.com/@instatunnel/multi-tenant-leakage-when-row-level-security-fails-in-saas-da25f40c788c
- https://milvus.io/blog/build-multi-tenancy-rag-with-milvus-best-practices-part-one.md
- https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/approaches/ai-ml
- https://tuananh.net/2026/01/22/i-was-wrong-about-ai-agent-sandboxing/
- https://northflank.com/blog/firecracker-vs-gvisor
- https://propelius.tech/blogs/tenant-data-isolation-patterns-and-anti-patterns/
