多租户 AI 信封加密边界:用 DEK、AAD 与密钥域阻断密文串租
全平台共用一把 KMS Key 只能证明数据被加密,不能阻止跨租户替换与误解密。本文设计租户密钥层级、上下文绑定、缓存和恢复验收。
本文要点
- 主题聚焦:AI、大模型、DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
对象存储显示 SSE-KMS 不代表多租户边界成立。如果应用用一把服务级 Key 解密所有对话、embedding 和微调 adapter,攻击者只要把租户 A 的密文换到租户 B 的对象键,错误代码就可能在合法 KMS 权限下解密。此前的 MCP 多租户隔离 约束请求、缓存和异步上下文,本篇把同一租户身份绑定到密文字节和密钥调用。
定义租户密钥域而不是一把万能 Key
冻结 ai.envelope.v1。每个租户和数据用途拥有独立逻辑密钥域,例如 tenant-42/conversation、tenant-42/vector、tenant-42/adapter。不一定为每条记录创建 KMS Key;可由受限 KEK 包装短寿命 DEK,但 KEK policy、grant 和账本必须让一个租户域无法解包另一个域的 DEK。NIST SP 800-57 的密钥用途、生命周期和归档原则应落实为可查询元数据,而不是只写在制度里。
数据使用带认证的加密模式,例如 AES-256-GCM。每次加密生成随机 nonce,同一 DEK 下绝不复用。AAD 至少覆盖租户 ID、数据用途、对象稳定 ID、schema version 和区域;密文从一个对象复制到另一个上下文时,认证必须失败。AWS KMS Encryption Context 也可以绑定这些字段,但它不是秘密,可能出现在审计日志中,不能放 Prompt、邮箱或原始文件名。
{
"schema": "ai.envelope.v1",
"tenant": "t_42",
"purpose": "conversation",
"objectId": "conv_01J...",
"region": "ap-east-1",
"keyVersion": "tenant-42/conversation/7",
"wrappedDek": "base64...",
"nonce": "base64...",
"ciphertext": "base64..."
}
tenant 必须来自已认证会话和服务端资源记录,不能信请求体。KMS 条件与应用 AAD 使用同一规范化值,大小写、Unicode、空值和版本转换规则随 schema 冻结。MCP Server 鉴权边界 中的服务身份只能获得“代表已授权租户请求解包”的能力,不能列出或批量解密所有租户。
控制明文、DEK 缓存与派生副本
DEK 只在内存中短暂存在,缓存键包含 tenant、purpose、KEK version 和 policy generation,并限制 TTL、条目数与实例。每次解密先从线性一致 tenant fence 取得短期 decryptLease,登记 operation ID、对象、generation、用途和到期时间;缓存命中也不能跳过 fence。租户停用、撤权或 key 禁用时,控制面原子推进 generation 并停止签发,擦除旧缓存,再取消或排空登记在旧代的 operation。
KMS 故障时,只有故障前已登记、策略允许继续且 lease 未过期的 operation 可以使用已缓存 DEK 完成;故障后到达的新请求和没有 operation 记录的工作一律失败,不能被重新定义为“在途”。fence 不可达、generation 落后或 lease 过期都默认拒绝,不能回退全局 key 或延长缓存。
对话摘要、向量、评测样本、导出文件和临时调试副本都要继承租户域;仅给主表加密而把 Prompt 写进 Trace 遥测 会绕过边界。消息队列传递密文与不可敏感的引用,worker 在授权后就地解密。明文不进入异常堆栈、交换分区、镜像层或跨租户批处理目录。
备份保存签名 manifest、每个稳定 object ID、密文摘要、wrapped DEK 摘要、完整 AAD、envelope revision 和 key version。恢复工具在目标租户与区域重新鉴权,对 manifest 与实际对象做全量集合差分,并逐项核对 tenant、AAD、revision、key version 和摘要;缺失、额外、未知 key 或跨租户条目为零后才开放读取,不能用抽样代替覆盖证明。“数据库恢复成功”不等于密钥边界恢复成功。云灾难恢复 的备用区域预置受限 KMS 路径,但不导出生产 KEK。
用替换攻击和恢复演练验收
测试夹具至少覆盖:把 A 密文放到 B 的对象 ID、替换 tenant 或 purpose、漏掉 AAD 字段、复用 nonce、使用已禁用 key version、伪造 wrapped DEK、让 KMS 超时、让一个 worker 保留旧缓存、从备份恢复到错误租户。所有跨域组合必须在解包策略或 AEAD 认证处失败,错误响应不能泄露 key 是否存在。
发布证据包括 key policy 摘要、KMS 决策、活跃 key version 覆盖率、旧缓存、密文 schema、恢复全量 manifest 报告与负向测试。日志仅保存随机对象 ID、key ID 和上下文摘要。AI 平台密钥轮换 的双凭证在此表现为旧、新 KEK 并存,但新写必须单调转向新版本。
迁移采用读旧写新。每个 rewrap.v1 事件带稳定 event ID、object ID、from/to key version、目标 envelope revision 和 expected old wrapped-DEK hash;worker 用 CAS 仅在对象仍匹配旧 revision/hash 时写新 wrapped DEK,响应丢失可按 event ID 幂等重放,并发业务更新获胜后旧 rewrap 成为 no-op。过渡期每个对象明确声明 key version,reader 只选对应 key,不逐把试解。
全量对象集合、revision 和备份 manifest 都无旧引用后,先停止旧写,再禁用旧 KEK并观察回滚窗口。回滚只能恢复未泄露旧 key 的显式读写策略,不能覆盖并发产生的新 envelope;已泄露 KEK 必须轮换并重新包装。
租户退场时,禁用其 KEK 可快速阻断在线解密,但“不可解密”与物理删除不是同一承诺。真正删除还要处理备份、日志和派生数据。可验证加密边界的目标,是让任意一段密文只能在正确租户、正确用途、正确对象和有效 key generation 的交集中被打开。
延伸阅读
Related