CMK必须存储在远程KMS(如AWS KMS)中,严禁存于MongoDB本地或代码里;kmsProviders中敏感凭证须动态注入;key vault集合需加密并严格授权;CMK轮换后旧密钥须保留至全部DEK迁移完成。

不能把客户主密钥(CMK)存在 MongoDB 本地或应用代码里——这是可查询加密中最敏感的凭据,一旦泄露,所有用它加密的 DEK 和字段都会被解密。
为什么必须用远程 KMS 存储 CMK
CMK 的唯一安全存放位置是受信的远程密钥管理系统(KMS),比如 AWS KMS、Azure Key Vault 或 Google Cloud KMS。MongoDB 本身不生成、不存储、不缓存 CMK;它只在运行时通过驱动调用 KMS API 解密 DEK。如果把 CMK 写进配置文件、环境变量甚至密钥保管库集合,等于把保险柜钥匙挂在锁芯上。
- CMK 泄露 = 所有字段级加密失效,且无法事后追溯或撤销影响
- 本地密钥提供程序仅用于开发测试,
localKMS 类型明确禁用于生产 - KMS 提供商强制执行访问控制(如 IAM 策略、RBAC)、审计日志和密钥轮换能力,这些 MongoDB 无法替代
kmsProviders 配置中哪些字段绝对不能硬编码
在初始化 ClientEncryption 时传入的 kmsProviders 对象,是驱动连接 KMS 的凭证入口。其中以下字段若写死,会直接破坏密钥隔离原则:
- AWS KMS:
accessKeyId和secretAccessKey必须通过 IAM 角色(EC2/ECS)或临时凭证(STS)注入,禁止出现在代码或 config.json 中 - Azure Key Vault:
tenantId、clientId、clientSecret应由托管标识(Managed Identity)自动获取,clientSecret绝不可明文提交到 Git - Google Cloud KMS:
email和privateKey必须来自服务账号 JSON 文件,且该文件路径应通过GCP_CREDENTIALS_FILE环境变量指定,而非写死在代码里
密钥保管库集合(key vault)本身也要加密
密钥保管库集合(默认名 encryption.__keyVault)虽然只存 DEK,但它的 keyMaterial 字段是用 CMK 加密后的二进制数据。如果这个集合未启用字段级加密或未限制读权限,攻击者可能批量导出所有 DEK 密文,再配合泄露的 CMK 批量解密。
- 务必在创建 key vault 集合后,立即在其
keyAltNames字段上建唯一索引:db.getCollection("__keyVault").createIndex({ "keyAltNames": 1 }, { unique: true }) - 禁止对 key vault 集合开放
find权限给业务角色;只允许ClientEncryption实例使用的专用数据库用户执行insert/find(且仅限匹配_id或keyAltNames的精确查询) - 不要在 key vault 集合上启用通配符索引或全文索引——这会暴露密钥元数据结构
CMK 轮换后旧密钥不能立即删除
CMK 支持轮换,但 MongoDB 不会自动重加密所有 DEK。你必须手动用新 CMK 创建新 DEK,并迁移字段加密策略。此时旧 CMK 仍需保留,直到确认:
- 所有使用旧 CMK 加密的 DEK 都已不再被任何集合 schema 引用
- 所有历史快照(尤其是 Atlas 备份)中,没有依赖该 CMK 加密的 DEK 文档
- 应用所有实例都完成重启并加载了新密钥配置(冷启动才生效,热重载不触发 KMS 凭证刷新)
删早了,哪怕只剩一个旧 DEK 文档没迁移,对应字段就永久变成 BinData 乱码,MongoDB 无法恢复。


















