30. MongoDB 静态加密与字段级加密

静态加密与压缩实战:WiredTiger 静态加密配置、KMS 与 KMIP 密钥管理、Client-Side FLE 客户端字段级加密、Queryable Encryption 可查询加密、snappy/zstd 块压缩与性能权衡

数据安全的最外一层是存储介质本身:磁盘被物理带走、备份文件泄露、云盘被误删后恢复,都会直接暴露明文数据。静态加密(Encryption at Rest)解决的是"文件层面"的安全,而字段级加密(Field-Level Encryption)进一步保护"字段层面"最敏感的单个值。前者由 WiredTiger 引擎内建,后者需要客户端配合密钥服务。本文从静态加密配置讲起,覆盖 KMS 密钥管理、Client-Side FLE 与 Queryable Encryption 两种字段级加密方案,以及 snappy/zstd 压缩与加密叠加时的性能权衡。

1. 静态加密与密钥层次

静态加密在数据库写入磁盘前把数据块加密,读取时解密,对应用透明。它保护的是磁盘文件、备份、快照等静止数据,与传输加密(TLS)形成互补:TLS 保护网络链路,静态加密保护存储介质。

1.1 密钥层次结构

生产级加密从不使用单一把密钥。MongoDB 静态加密采用"主密钥 + 数据加密密钥"的层次:

  • 主密钥(Master Key):保存在外部密钥管理系统(KMS),不落在本地磁盘
  • 数据加密密钥(DEK):由主密钥加密后存储在本地,实际用于加解密数据库文件
  • 数据库文件使用 DEK 加密,DEK 本身又由主密钥保护
// 概念示意:密钥层次
// 外部 KMS / 本地 keyfile
//   └── 主密钥(Master Key)
//         └── 数据加密密钥(DEK,加密后存本地)
//               └── 数据库数据文件

1.2 静态加密与传输加密的区别

维度TLS 传输加密静态加密
保护对象网络传输中的数据磁盘上的数据
生效位置客户端与服务端之间存储引擎写盘前
运维影响证书管理密钥管理与启动依赖
失效场景抓包、中间人磁盘丢失、备份泄露

1.3 常见威胁与防护层级

威胁静态加密字段级加密TLSRBAC
磁盘被物理带走有效有效无效无效
备份文件泄露有效有效无效无效
数据库管理员越权读取无效有效无效部分
网络抓包无效无效有效无效
恶意应用提权部分部分无效有效

可以看到,任何单一手段都无法覆盖全部威胁。静态加密挡外部物理攻击,字段级加密挡内部越权,TLS 挡网络窃听,RBAC 挡访问越权,四者叠加才能形成完整防线。

重要:静态加密不能替代访问控制。数据库文件加密后,合法进程仍能读取明文,权限、认证、审计仍然不可或缺。加密是"纵深防御"的一层,不是全部。

2. WiredTiger 静态加密配置

MongoDB Enterprise 支持内建的静态加密,通过 WiredTiger 引擎在数据写盘前加密。启用后,数据文件、日志、临时文件都会被加密。

2.1 本地密钥文件方式

# 生成 96 字节的 base64 密钥文件(最小 64 字节)
openssl rand -base64 96 > /etc/mongodb-keys/mongodb.key
chmod 600 /etc/mongodb-keys/mongodb.key
# mongod.conf
security:
  enableEncryption: true
  encryptionKeyFile: /etc/mongodb-keys/mongodb.key
  # encryptionCipherMode: AES256-CBC   # 或 AES256-GCM

首次启动时 MongoDB 会初始化加密密钥;之后每次启动都必须提供同一个密钥文件,否则无法读取数据。密钥文件丢失等于数据丢失,务必备份或接入 KMS。

// 验证加密是否生效
db.serverStatus().storageEngine.wiredTiger
// 输出中包含 encrypted 相关字段即说明已启用

2.2 KMIP 与 KMS 集成

生产环境推荐使用外部密钥管理:本地 keyfile 是"密钥和密文同机",泄露任一即全盘失守。KMIP(Key Management Interoperability Protocol)把主密钥托管到独立密钥服务器。

# mongod.conf:KMIP 方式
security:
  enableEncryption: true
  kmip:
    keyIdentifier: "1a2b3c4d"
    serverName: "kmip-server.internal"
    port: 5696
    clientCertificateFile: /etc/mongodb-keys/kmip-client.pem
    serverCAFile: /etc/mongodb-keys/kmip-ca.pem
密钥管理方式主密钥存储位置适用规模风险点
本地 keyfile数据库服务器开发测试密钥与数据同机
KMIP独立密钥服务器企业合规依赖 KMIP 服务可用性
云 KMSAWS/Azure/GCP 托管云部署依赖云厂商可用性

决策铁律:密钥管理的本质是"把密钥与密文分离"。本地 keyfile 只适合开发环境;生产环境至少使用独立的密钥服务器或云 KMS,并配置密钥轮换与访问审计。

3. 密钥轮换与运维

密钥轮换降低单密钥长期使用被破解的风险。静态加密的 DEK 可以随时重新生成(re-encrypt),主密钥轮换则需要重新包装 DEK。

// 重新生成静态加密密钥(DEK 层,可在线完成)
// 需要先在目标服务器放置新的 keyfile
# 主密钥轮换步骤(本地 keyfile 场景)
# 1. 准备新密钥文件 new-key.key
# 2. 停机维护窗口
# 3. 启动时指定 --encryptionKeyFile new-key.key
# 4. 首次启动会使用新密钥重新包装 DEK

3.1 云 KMS 集成

云上部署最自然的密钥管理方式是接入云 KMS。MongoDB Enterprise 支持 AWS KMS、Azure Key Vault、Google Cloud KMS 作为主密钥提供方。

# mongod.conf:AWS KMS 方式
security:
  enableEncryption: true
  awsKms:
    keyId: "arn:aws:kms:us-east-1:123456789012:key/abc123"
    secretAccessKey: "..."
    accessKeyId: "..."
    region: "us-east-1"

云 KMS 的核心优势是主密钥不落数据库服务器、权限由云 IAM 统一管控、密钥轮换由云侧调度。代价是数据库启动与访问依赖云 KMS 的可用性,网络中断时可能无法读取加密数据。因此云 KMS 场景要配合本地缓存策略,评估可用性风险。

3.2 备份与密钥的绑定关系

加密数据库的备份恢复有一项硬约束:备份文件必须连同当时的主密钥一起恢复。密钥轮换后,旧备份需要旧密钥解密,新数据用新密钥。运维上要建立"备份快照 + 密钥版本"的对应台账,否则某个时间点的备份可能永远无法恢复。

# 恢复加密备份(示意)
# 1. 恢复备份文件到数据目录
# 2. 使用备份当时的主密钥启动 mongod
# mongod --config /etc/mongod-restore.conf --security.enableEncryption true \
#   --security.encryptionKeyFile /keys/backup-era.key

运维要点:

  • 密钥轮换必须有维护窗口,且演练过失败恢复
  • 密钥文件权限严格 600,属主为 mongod 用户
  • 备份中的加密数据依赖同一密钥才能恢复,密钥丢失即备份失效
  • 审计日志记录密钥轮换事件与访问者

4. 客户端字段级加密 Client-Side FLE

静态加密保护的是整个数据库文件,而字段级加密保护的是字段本身:数据库管理员与运维人员看到的也是密文。MongoDB 的 Client-Side Field Level Encryption(CSFLE)在驱动层加解密,服务端只存储密文。

4.1 CSFLE 架构

  • Key Vault 集合:存储数据加密密钥(DEK),本身加密
  • 加密密钥管理:主密钥托管在 KMS(本地/云)
  • 自动加密:驱动根据 JSON Schema 自动加解密指定字段
  • mongocryptd:辅助进程,用于自动加密时的查询改写(可禁用)
// 建立 Key Vault 集合(mongosh 中执行)
db.getSiblingDB("encryption").createCollection("__keyVault")

// 创建数据加密密钥(示意)
// 需要配置 KMS provider 后调用

4.2 自动加密的 Schema

客户端通过 JSON Schema 声明哪些字段需要加密、使用哪种加密算法:

// 驱动侧自动加密 Schema(示意,Node.js 驱动)
const schema = {
  bsonType: "object",
  properties: {
    ssn: {
      encrypt: {
        bsonType: "string",
        algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
      }
    },
    salary: {
      encrypt: {
        bsonType: "decimal",
        algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
      }
    }
  }
}
加密算法特性适用字段
Deterministic相同明文得到相同密文等值查询字段(如 ssn、邮箱)
Random每次加密结果不同无查询需求的敏感字段(如薪资)

4.3 CSFLE 的限制

确定性加密支持等值查询但会泄露相等性信息,随机加密安全性更高但不支持查询。服务端对加密字段的索引能力受限,范围查询、排序、聚合通常无法在密文上执行。使用前要评估查询模式,避免"加密后应用不可用"。

5. Queryable Encryption 可查询加密

MongoDB 6.0 引入的 Queryable Encryption(可查询加密)解决了 FLE 的最大痛点:加密字段上的等值查询。它基于"可查询加密方案"(Indexed / Unindexed 两种字段类型),允许对加密字段执行等值匹配,同时密文不泄露明文。

// Queryable Encryption Schema 定义(示意)
const qeSchema = {
  bsonType: "object",
  properties: {
    email: {
      encrypt: {
        bsonType: "string",
        keyId: [ "data-key-id" ],
        algorithm: "Indexed",          // 支持等值查询
        queryType: "equality"
      }
    },
    medicalRecord: {
      encrypt: {
        bsonType: "object",
        keyId: [ "data-key-id" ],
        algorithm: "Unindexed"         // 仅存储,不支持查询
      }
    }
  }
}
方案支持等值查询性能适用版本
CSFLE 确定性加密是低开销4.2+
CSFLE 随机加密否低开销4.2+
Queryable Encryption Indexed是较高(可查询加密代价)6.0+
Queryable Encryption Unindexed否低开销6.0+

Queryable Encryption 使用可查询加密(Queryable Encryption)技术为加密字段建立特殊索引结构,服务端在密文上执行等值查找而无法还原明文。它适合"必须加密且必须可查询"的高敏感字段。

// 关键约束:加密字段的查询必须是精确等值,不支持范围与正则
db.patients.find({ email: "user@example.com" })   // 支持
db.patients.find({ email: { $regex: /example/ } }) // 不支持

重要:字段级加密是把双刃剑。加密越强,查询能力越弱,性能开销越高。上线前必须完成字段级加密的查询模式盘点:哪些字段需要可查询、哪些只需保密存储,据此选择 Deterministic / Random / Indexed / Unindexed。

6. 压缩机制与加密叠加

WiredTiger 默认使用块级压缩减少磁盘占用。静态加密与压缩同时启用时,压缩发生在加密之前,两层可以叠加。

6.1 压缩算法选择

# mongod.conf:全局默认压缩算法
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 8
    collectionConfig:
      blockCompressor: zstd    # snappy / zstd / zlib
// 创建集合时指定压缩算法
db.createCollection("orders", {
  storageEngine: { wiredTiger: { configString: "block_compressor=zstd" } }
})
压缩算法压缩率压缩速度解压速度适用场景
snappy中快快读写均衡、默认值
zstd高较快快存档、日志类冷数据
zlib高慢中极致压缩、写少读少

6.2 加密与压缩的叠加效果

加密后的数据接近随机分布,几乎无法再压缩,因此压缩必须先于加密执行。WiredTiger 的处理顺序是"先压缩、后加密",两者收益叠加:压缩减小存储,加密保证安全。

组合存储占用性能影响安全等级
仅压缩小轻微 CPU明文
仅加密大(无压缩)轻微 CPU密文
压缩 + 加密小两个阶段的 CPU 开销密文

压缩与加密的权衡要点:加密本身有 CPU 开销,叠加压缩后写路径更重;对写入密集型业务,评估是否值得为冷数据单独压缩。日志类数据用 zstd 压缩率高,热点数据用 snappy 保证读写延迟。

7. 加密方案选型总结

静态加密与字段级加密解决不同层级的威胁,选型应按数据敏感度分层:

  • 全库静态加密:低维护成本,防磁盘丢失与备份泄露,是生产基线
  • CSFLE 确定性加密:需要等值查询的高敏感字段(证件号、邮箱)
  • Queryable Encryption:既要加密又必须可查询的字段(医疗、金融)
  • 随机加密 / Unindexed:无需查询的机密字段(薪资、隐私备注)
方案保护粒度查询能力运维复杂度性能开销
静态加密文件不受影响低低
CSFLE 确定性字段等值中中
CSFLE 随机字段无中低
Queryable Encryption字段等值(Indexed)高较高

决策铁律:加密选型的起点是数据分类。先按敏感度给字段分档,再决定"要不要加密、要不要可查询、用什么算法"。所有字段一刀切上最重加密,只会换来不可用的查询和失控的延迟。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「mongodb」更多文章

  1. 33. MongoDB 地理空间与全文检索
  2. 32. MongoDB 批量写入与吞吐优化
  3. 31. MongoDB 读写关注与一致性