数据安全的最外一层是存储介质本身:磁盘被物理带走、备份文件泄露、云盘被误删后恢复,都会直接暴露明文数据。静态加密(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 常见威胁与防护层级
| 威胁 | 静态加密 | 字段级加密 | TLS | RBAC |
|---|---|---|---|---|
| 磁盘被物理带走 | 有效 | 有效 | 无效 | 无效 |
| 备份文件泄露 | 有效 | 有效 | 无效 | 无效 |
| 数据库管理员越权读取 | 无效 | 有效 | 无效 | 部分 |
| 网络抓包 | 无效 | 无效 | 有效 | 无效 |
| 恶意应用提权 | 部分 | 部分 | 无效 | 有效 |
可以看到,任何单一手段都无法覆盖全部威胁。静态加密挡外部物理攻击,字段级加密挡内部越权,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 服务可用性 |
| 云 KMS | AWS/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) | 高 | 较高 |
决策铁律:加密选型的起点是数据分类。先按敏感度给字段分档,再决定"要不要加密、要不要可查询、用什么算法"。所有字段一刀切上最重加密,只会换来不可用的查询和失控的延迟。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。