2023 年,某互联网医疗公司的数据库备份磁带在运输途中丢失。磁带里存储着 300 万条患者病历。事后调查发现,数据库开启了传输层加密(TLS),但磁带上的备份文件是明文——没有做静态加密(Encryption at Rest)。攻击者不需要破解任何密码,只需要一台磁带驱动器就能读取全部数据。
这个事故暴露了一个常见误区:很多团队把”加密”当作一个单一的事情来处理,认为”上了 HTTPS 就安全了”。但加密至少涉及三个完全不同的层次,每个层次对应不同的威胁模型,解决不同的安全问题。混淆这三个层次,就会出现上面那种”TLS 开了但数据还是泄露了”的情况。
本文回答一个核心问题:静态加密(Encryption at Rest)、传输加密(Encryption in Transit)、应用层加密(Application-Level Encryption)分别解决什么威胁模型?密钥管理服务(Key Management Service,KMS)该怎么设计?
一、问题场景
在讨论加密方案之前,先明确几个典型的威胁场景。
场景一:磁盘被盗或备份泄露。数据中心退役硬盘、云存储桶配置错误、备份磁带丢失——数据以文件形式存储在物理介质上,攻击者通过物理接触或配置漏洞获取原始文件。这个场景需要静态加密来防护。
场景二:网络窃听与中间人攻击。攻击者在网络链路上抓包,读取客户端与服务端之间传输的明文数据。公共 Wi-Fi、未加密的内部网络、BGP 劫持都属于这类威胁。这个场景需要传输加密来防护。
场景三:内部人员或应用层漏洞。数据库管理员(DBA)直接查询敏感字段、应用层 SQL 注入(SQL Injection)导致数据泄露、日志中意外打印了用户手机号。这个场景需要应用层加密——数据在被应用处理之前就已经是密文,即使被拖库,攻击者拿到的也是加密后的值。
场景四:云厂商不可信。在多租户云环境中,用户希望即使云厂商的运维人员也无法读取数据。这是一个更极端的威胁模型,涉及客户端加密(Client-Side Encryption)和使用中加密(Encryption in Use),甚至需要同态加密(Homomorphic Encryption)。
这四个场景对应的加密层次和密钥管理策略完全不同。把它们混为一谈,要么过度加密浪费性能,要么关键层次遗漏留下安全缺口。
二、加密的三个层次
2.1 层次总览
数据在整个生命周期中存在三种状态,每种状态对应一个加密层次:
| 数据状态 | 加密层次 | 威胁模型 | 典型技术 |
|---|---|---|---|
| 静态(At Rest) | 磁盘/存储层加密 | 物理窃取、备份泄露、存储桶误配 | TDE、LUKS、S3 SSE |
| 传输中(In Transit) | 网络层加密 | 网络窃听、中间人攻击(MITM) | TLS 1.3、mTLS、IPSec |
| 使用中(In Use) | 应用层加密 | DBA 越权、SQL 注入、日志泄露 | 字段级加密、信封加密、FHE |
2.2 三层的关系
这三个层次不是替代关系,而是叠加关系。一个合规的金融系统通常需要同时启用三层:
- 静态加密保证即使磁盘被盗,数据不可读;
- 传输加密保证网络传输过程中数据不可窃听;
- 应用层加密保证即使数据库被拖库或 DBA 直接查询,敏感字段仍然是密文。
三层叠加的代价是性能开销和运维复杂度。后面章节会逐层分析每一层的实现方式、性能影响和工程取舍。
三、静态加密与 TDE
3.1 静态加密的原理
静态加密(Encryption at Rest)在数据写入存储介质时加密,读取时解密。对上层应用完全透明——应用不需要修改任何代码。
实现方式分两大类:
全盘加密(Full Disk Encryption,FDE):在操作系统层面对整个磁盘分区加密。Linux 上典型实现是 LUKS(Linux Unified Key Setup)+ dm-crypt。优点是对所有应用透明;缺点是粒度太粗,无法针对不同数据库、不同表设置不同的密钥。
透明数据加密(Transparent Data Encryption,TDE):在数据库引擎层面实现,对数据文件(data files)、日志文件(wal/redo log)和备份文件进行加密。应用通过正常的 SQL 接口读写数据,加解密由数据库引擎自动完成。
3.2 TDE 的实现差异
不同数据库的 TDE 实现有显著差异:
SQL Server TDE:最早提供 TDE 支持(2008 年),使用数据库加密密钥(Database Encryption Key,DEK)加密数据文件,DEK 由服务器证书保护。加密粒度为数据库级别。TempDB 在任何一个数据库启用 TDE 后会被自动加密。
PostgreSQL TDE:社区版本长期不支持原生
TDE。PostgreSQL 16 引入了集群级别的 TDE 支持(通过
pg_tde
扩展或编译时选项)。加密在页面(page)级别进行,使用
AES-256。密钥管理需要外部集成。
-- PostgreSQL: 启用 pg_tde 扩展(示例)
CREATE EXTENSION pg_tde;
-- 配置密钥提供者(以 Vault 为例)
SELECT pg_tde_add_key_provider_vault_v2(
'vault_provider',
'https://vault.internal:8200',
'secret/data/pg-tde',
'vault-token-xxxx'
);
-- 设置主密钥
SELECT pg_tde_set_principal_key('main_key', 'vault_provider');MySQL InnoDB TDE:MySQL 5.7.11 开始支持表空间级别的 TDE。使用两层密钥架构:表空间密钥(Tablespace Key)由主密钥(Master Key)加密。主密钥存储在 keyring 插件中,支持对接外部 KMS。
-- MySQL: 启用 InnoDB 表空间加密
ALTER TABLE patients ENCRYPTION='Y';
-- 查看加密状态
SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS
FROM INFORMATION_SCHEMA.TABLES
WHERE CREATE_OPTIONS LIKE '%ENCRYPTION%';3.3 TDE 的性能影响
TDE 的性能影响是架构师最关心的问题之一。以下是基于公开基准测试的数据:
| 数据库 | 测试负载 | 性能下降 | 说明 |
|---|---|---|---|
| SQL Server 2019 | OLTP(TPC-C) | 3%-5% | 已启用硬件加速(AES-NI) |
| MySQL 8.0 | sysbench read-write | 5%-8% | InnoDB 表空间加密 |
| PostgreSQL 16 | pgbench | 5%-10% | pg_tde 扩展,取决于 I/O 模式 |
几个关键观察:
AES-NI 硬件加速是关键。现代 CPU 几乎都支持 AES-NI(AES New Instructions)指令集,能将 AES 加解密的 CPU 开销降低 90% 以上。没有 AES-NI 的老旧硬件上,TDE 的性能下降可能达到 20%-30%。
I/O 密集型负载受影响更大。TDE 的开销主要在数据页面的加解密上。如果工作集(working set)完全在内存缓冲池(buffer pool)中,加解密只在页面换入换出时发生,影响很小。如果频繁发生磁盘 I/O,影响会明显增大。
备份和恢复时间增加。加密备份的写入和恢复都需要额外的 CPU 计算。在大型数据库(TB 级别)上,备份时间可能增加 10%-20%。
3.4 TDE 不保护什么
TDE 有一个常见的误解:开启 TDE 后数据就”安全”了。实际上 TDE 只防护一种威胁——物理介质被窃取后的数据泄露。以下场景 TDE 无能为力:
- DBA 通过正常的数据库连接查询数据,看到的是明文;
- SQL 注入攻击通过应用层获取数据,看到的是明文;
- 数据库内存中的数据是明文(内存转储攻击);
- 数据库日志中可能打印出明文查询和结果。
要防护这些场景,需要应用层加密,这在第七节讨论。
四、传输加密
4.1 TLS 1.3 的核心改进
传输层安全(Transport Layer Security,TLS)是传输加密的事实标准。TLS 1.3(RFC 8446,2018 年发布)相比 TLS 1.2 有几个关键改进:
- 握手延迟降低:TLS 1.3 将握手从 2-RTT 降低到 1-RTT,支持 0-RTT 恢复(但有重放攻击风险);
- 移除不安全的密码套件:删除了 RC4、3DES、CBC 模式等已被证明不安全的算法,只保留 AEAD(Authenticated Encryption with Associated Data)密码套件;
- 前向保密(Forward Secrecy)强制启用:所有密钥交换都基于临时(ephemeral)Diffie-Hellman,即使长期私钥泄露,历史通信也不会被解密。
4.2 mTLS 与零信任架构
单向 TLS(标准 HTTPS)只验证服务端的身份。在零信任架构(Zero Trust Architecture)中,客户端也需要向服务端证明身份,这就是双向 TLS(mutual TLS,mTLS)。
mTLS 的典型应用场景:
- 微服务之间的服务间通信(service-to-service communication);
- 服务网格(Service Mesh)中的数据平面加密,例如 Istio 的自动 mTLS;
- API 网关与后端服务之间的通信。
关于零信任架构的完整讨论,参见零信任架构。
4.3 证书管理的工程挑战
TLS 的安全性依赖于证书的正确管理。证书过期是生产事故的常见原因——2020 年 Microsoft Teams 全球宕机的根因就是一张中间证书(Intermediate Certificate)过期未续期。
证书管理的关键实践:
- 自动化签发与续期:使用 cert-manager(Kubernetes 环境)或 ACME 协议(Let’s Encrypt)实现证书的自动签发和续期;
- 短有效期证书:将证书有效期从传统的 1-2 年缩短到 90 天甚至更短。Istio 的 mTLS 证书默认有效期为 24 小时;
- 证书监控告警:监控所有证书的到期时间,至少提前 30 天告警。
关于证书管理的完整讨论,参见密钥与证书管理。
4.4 内部网络也要加密
一个常见的错误假设是”内部网络是安全的,不需要加密”。2013 年 NSA 被曝光的 MUSCULAR 项目,正是通过窃听 Google 数据中心之间的内部光纤链路获取数据——当时 Google 的数据中心之间通信是明文的。
这个事件之后,Google 全面启用了数据中心间的传输加密,并公开了其 ALTS(Application Layer Transport Security)协议。ALTS 与 TLS 的核心区别在于身份模型——ALTS 使用基于服务账号的身份认证,而不是基于主机名的证书验证,更适合大规模微服务环境。
现在的行业共识是:即使在私有网络内部,服务间通信也应该加密。
# Istio: 启用严格 mTLS 模式
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT4.4 数据库连接加密
很多团队忽略了应用到数据库之间的连接加密。根据 2023 年 Percona 的调查,超过 40% 的 MySQL 和 PostgreSQL 生产实例的客户端连接未启用 SSL。这意味着应用服务器和数据库之间的查询和结果在网络上以明文传输。
以 PostgreSQL 为例:
# postgresql.conf: 要求所有连接使用 SSL
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
ssl_ca_file = '/etc/ssl/certs/ca.crt'# pg_hba.conf: 拒绝非 SSL 连接
hostssl all all 0.0.0.0/0 scram-sha-256
hostnossl all all 0.0.0.0/0 reject在连接字符串中也需要明确指定 SSL 模式:
postgresql://app:password@db.internal:5432/mydb?sslmode=verify-full&sslrootcert=/etc/ssl/certs/ca.crt
sslmode=verify-full
会同时验证服务端证书的有效性和主机名匹配,是最安全的模式。很多生产事故是因为使用了
sslmode=require——它只加密传输但不验证服务端身份,无法防止中间人攻击。
五、信封加密
5.1 为什么需要信封加密
直觉上,加密只需要一个密钥就够了:用一个主密钥(Master Key)直接加密所有数据。但这种方案有几个严重问题:
- 密钥轮换代价高。轮换密钥时需要用旧密钥解密全部数据,再用新密钥重新加密。数据量大时这个过程可能持续数小时甚至数天。
- 密钥泄露的爆炸半径大。一个密钥泄露,所有数据都暴露。
- 远程 KMS 的性能瓶颈。如果每次加解密都需要调用远程 KMS,网络延迟和 KMS 的吞吐量会成为瓶颈。
信封加密(Envelope Encryption)通过引入两层密钥架构来解决这些问题。
5.2 信封加密的原理
信封加密使用两层密钥:
- 数据加密密钥(Data Encryption Key,DEK):用于实际加密数据。每个数据对象(文件、数据库记录、消息)使用独立的 DEK,DEK 在本地生成和使用。
- 密钥加密密钥(Key Encryption Key,KEK):用于加密 DEK。KEK 存储在 KMS 中,永远不会离开 KMS 的安全边界(在硬件安全模块 HSM 中尤其如此)。
加密流程:
- 应用向 KMS 请求生成一个 DEK(或本地生成 DEK);
- KMS 返回 DEK 的明文和密文两个版本(明文 DEK 用 KEK 加密后得到密文 DEK);
- 应用使用明文 DEK 加密数据;
- 应用将密文 DEK 与密文数据一起存储;
- 应用立即丢弃明文 DEK(不持久化)。
解密流程:
- 应用从存储中读取密文数据和密文 DEK;
- 应用将密文 DEK 发送给 KMS;
- KMS 使用 KEK 解密,返回明文 DEK;
- 应用使用明文 DEK 解密数据;
- 应用使用后立即丢弃明文 DEK。
sequenceDiagram
participant App as 应用服务
participant KMS as KMS(密钥管理服务)
participant Store as 存储(S3/DB)
Note over App,Store: 加密流程
App->>KMS: GenerateDataKey(KEK-ID)
KMS-->>App: {plaintext DEK, encrypted DEK}
App->>App: 使用 plaintext DEK 加密数据
App->>Store: 存储 {encrypted data, encrypted DEK}
App->>App: 丢弃 plaintext DEK
Note over App,Store: 解密流程
App->>Store: 读取 {encrypted data, encrypted DEK}
Store-->>App: {encrypted data, encrypted DEK}
App->>KMS: Decrypt(encrypted DEK)
KMS-->>App: plaintext DEK
App->>App: 使用 plaintext DEK 解密数据
App->>App: 丢弃 plaintext DEK
5.3 信封加密的优势
密钥轮换高效:轮换 KEK 时,只需要用新 KEK 重新加密 DEK(几百字节),不需要重新加密实际数据(可能是 GB 级别)。
爆炸半径可控:每个数据对象使用独立的 DEK。即使某个 DEK 泄露,只影响对应的那一个数据对象。
性能优化:实际的数据加解密在本地完成(使用 AES-256-GCM 等对称算法),只有 DEK 的加解密需要调用远程 KMS。KMS 调用次数大幅减少。
5.4 信封加密的代码实现
以下是使用 Go 语言和 AWS KMS 实现信封加密的完整示例:
package envelope
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
)
// EncryptedPayload 包含密文数据和加密后的 DEK
type EncryptedPayload struct {
Ciphertext []byte // AES-GCM 加密后的数据(含 nonce)
EncryptedDEK []byte // KMS 加密后的 DEK
KeyID string // KEK 的 ARN
}
// Encrypt 使用信封加密对数据进行加密
func Encrypt(ctx context.Context, client *kms.Client, keyID string, plaintext []byte) (*EncryptedPayload, error) {
// 1. 向 KMS 请求生成 DEK
genOutput, err := client.GenerateDataKey(ctx, &kms.GenerateDataKeyInput{
KeyId: &keyID,
KeySpec: types.DataKeySpecAes256,
})
if err != nil {
return nil, fmt.Errorf("生成 DEK 失败: %w", err)
}
// 2. 使用明文 DEK 加密数据
block, err := aes.NewCipher(genOutput.Plaintext)
if err != nil {
return nil, fmt.Errorf("创建 AES cipher 失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("创建 GCM 失败: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, fmt.Errorf("生成 nonce 失败: %w", err)
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
// 3. 清除内存中的明文 DEK
for i := range genOutput.Plaintext {
genOutput.Plaintext[i] = 0
}
return &EncryptedPayload{
Ciphertext: ciphertext,
EncryptedDEK: genOutput.CiphertextBlob,
KeyID: keyID,
}, nil
}
// Decrypt 解密信封加密的数据
func Decrypt(ctx context.Context, client *kms.Client, payload *EncryptedPayload) ([]byte, error) {
// 1. 向 KMS 请求解密 DEK
decOutput, err := client.Decrypt(ctx, &kms.DecryptInput{
CiphertextBlob: payload.EncryptedDEK,
KeyId: &payload.KeyID,
})
if err != nil {
return nil, fmt.Errorf("解密 DEK 失败: %w", err)
}
// 2. 使用明文 DEK 解密数据
block, err := aes.NewCipher(decOutput.Plaintext)
if err != nil {
return nil, fmt.Errorf("创建 AES cipher 失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("创建 GCM 失败: %w", err)
}
nonceSize := gcm.NonceSize()
if len(payload.Ciphertext) < nonceSize {
return nil, fmt.Errorf("密文长度不足")
}
nonce, ciphertext := payload.Ciphertext[:nonceSize], payload.Ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("解密数据失败: %w", err)
}
// 3. 清除内存中的明文 DEK
for i := range decOutput.Plaintext {
decOutput.Plaintext[i] = 0
}
return plaintext, nil
}代码中有几个值得注意的工程细节:
- nonce 与密文一起存储:AES-GCM 要求每个 nonce 唯一但不需要保密,直接拼接在密文前面是常见做法。
- 明文 DEK
使用后清零:通过覆写内存中的密钥字节来降低内存转储攻击的风险。Go
的垃圾回收器可能在清零前已经复制了数据,这是一个已知限制——在安全要求极高的场景中需要使用
CGo 调用
mlock和memset_s。 - 错误处理不泄露密钥信息:错误消息只包含操作描述,不包含密钥材料。
5.5 HashiCorp Vault 的 Transit 引擎
除了云厂商的 KMS,HashiCorp Vault 的 Transit Secrets Engine 也是信封加密的常用方案,特别适合混合云和多云环境:
# 启用 Transit 引擎
vault secrets enable transit
# 创建加密密钥
vault write -f transit/keys/payment-data type=aes256-gcm96
# 加密数据
vault write transit/encrypt/payment-data \
plaintext=$(echo -n "card-number-4111-1111-1111-1111" | base64)
# 输出示例:
# ciphertext vault:v1:AbcDeFgHiJkLmNoPqRsTuVwXyZ...
# 解密数据
vault write transit/decrypt/payment-data \
ciphertext="vault:v1:AbcDeFgHiJkLmNoPqRsTuVwXyZ..."
# 密钥轮换(不需要重新加密数据)
vault write -f transit/keys/payment-data/rotateVault Transit 的密文前缀 vault:v1:
中的版本号 v1
标识了使用哪个版本的密钥加密。轮换密钥后新数据使用
v2,旧数据仍然可以用 v1
解密。这种版本化密钥的机制使得密钥轮换对应用完全透明。
六、KMS 架构设计
6.1 KMS 的核心职责
密钥管理服务(KMS)是整个加密架构的核心枢纽。它的职责包括:
- 密钥生命周期管理:生成、存储、轮换、吊销、销毁密钥;
- 密钥层级管理:维护根密钥(Root Key)、主密钥(Master Key)、数据密钥(Data Key)的层级关系;
- 访问控制:基于策略控制哪些服务、哪些用户可以使用哪些密钥执行哪些操作;
- 审计日志:记录所有密钥操作,用于合规审计。
6.2 密钥层级架构
一个典型的企业级 KMS 使用三层或四层密钥架构:
graph TB
subgraph HSM["硬件安全模块(HSM)"]
RK["根密钥(Root Key)<br/>永不离开 HSM"]
end
subgraph KMS_Layer["KMS 服务层"]
MK1["主密钥 A(Master Key)<br/>订单服务"]
MK2["主密钥 B(Master Key)<br/>支付服务"]
MK3["主密钥 C(Master Key)<br/>用户服务"]
end
subgraph App_Layer["应用层"]
DEK1["DEK-1<br/>订单数据加密"]
DEK2["DEK-2<br/>支付卡号加密"]
DEK3["DEK-3<br/>身份证号加密"]
DEK4["DEK-4<br/>手机号加密"]
end
RK -->|加密保护| MK1
RK -->|加密保护| MK2
RK -->|加密保护| MK3
MK1 -->|加密保护| DEK1
MK2 -->|加密保护| DEK2
MK3 -->|加密保护| DEK3
MK3 -->|加密保护| DEK4
根密钥(Root Key):存储在硬件安全模块(Hardware Security Module,HSM)中,永远不会以明文形式离开 HSM。AWS 使用 CloudHSM,GCP 使用 Cloud HSM,自建方案通常使用 Thales Luna 或 Entrust nShield 系列。
主密钥(Master Key):每个业务域或服务拥有独立的主密钥。主密钥由根密钥加密保护,存储在 KMS 数据库中。主密钥可以轮换,轮换时只需要用根密钥重新加密新版本的主密钥。
数据密钥(Data Key / DEK):由主密钥加密保护,用于实际数据加密。DEK 的密文与数据一起存储,明文只在内存中短暂存在。
6.3 KMS 的高可用设计
KMS 是加密架构的单点——如果 KMS 不可用,所有依赖加密的服务都无法正常工作。因此 KMS 的高可用设计至关重要:
多区域部署:KMS 至少部署在两个可用区(Availability Zone),主密钥在多个区域有副本。AWS KMS 默认在同一区域的多个可用区间复制密钥。
缓存策略:DEK 的解密结果可以在应用内存中缓存一段时间(例如 5 分钟),减少对 KMS 的调用频率。缓存 TTL 需要在安全性和可用性之间取舍——TTL 越短越安全(密钥轮换生效更快),但 KMS 调用量越大。
降级策略:当 KMS 完全不可用时,应用需要有明确的降级策略。常见做法是: - 读操作:使用本地缓存的 DEK 继续解密(缓存有效期内); - 写操作:拒绝写入或写入到临时未加密队列,KMS 恢复后补加密。
6.4 KMS 的访问控制
KMS 的访问控制策略直接决定了加密体系的安全强度。最小权限原则(Principle of Least Privilege)是核心指导思想:
- 按服务授权:每个微服务只能访问自己业务域的主密钥。订单服务无法使用支付服务的密钥。
- 按操作授权:加密和解密权限分离。数据生产者只需要加密权限,数据消费者只需要解密权限。密钥管理操作(轮换、销毁)只有安全团队拥有。
- 按环境隔离:开发环境、测试环境、生产环境使用完全不同的密钥层级,互不可访问。
以 AWS KMS 的密钥策略为例:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPaymentServiceEncryptOnly",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789:role/payment-service"
},
"Action": [
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
},
{
"Sid": "AllowPaymentServiceDecrypt",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789:role/payment-service"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:service": "payment"
}
}
}
]
}上述策略中使用了加密上下文(Encryption Context)——一个键值对元数据,在加密时绑定到密文。解密时必须提供相同的加密上下文才能成功。这提供了额外的访问控制维度,即使拥有解密权限,如果无法提供正确的加密上下文也无法解密数据。
6.5 密钥轮换策略
密钥轮换是密钥管理中最复杂的操作之一。不同层级的密钥轮换策略不同:
| 密钥层级 | 轮换周期 | 轮换方式 | 影响 |
|---|---|---|---|
| 根密钥 | 1-3 年 | HSM 内部轮换 | 需要重新加密所有主密钥 |
| 主密钥 | 90 天-1 年 | KMS 自动轮换 | 需要重新加密所有 DEK |
| 数据密钥 | 每次使用新生成 | 自动 | 无影响 |
AWS KMS 支持自动密钥轮换,每年自动生成新版本的密钥材料,旧版本保留用于解密历史数据:
# AWS KMS: 启用自动密钥轮换
aws kms enable-key-rotation --key-id arn:aws:kms:us-east-1:123456789:key/abcd-1234
# 查看轮换状态
aws kms get-key-rotation-status --key-id arn:aws:kms:us-east-1:123456789:key/abcd-1234七、应用层加密
7.1 字段级加密
应用层加密(Application-Level Encryption)在应用代码中对特定字段进行加密,然后将密文写入数据库。这是防护 DBA 越权和 SQL 注入的最有效手段。
典型的加密字段包括:
- 身份证号、护照号等身份标识;
- 银行卡号(PCI DSS 要求);
- 手机号、邮箱地址等联系方式;
- 医疗诊断记录(HIPAA 要求)。
字段级加密的数据库存储示例:
CREATE TABLE patients (
id BIGINT PRIMARY KEY,
name VARCHAR(100), -- 明文(非敏感)
id_number BYTEA, -- 密文(信封加密后的身份证号)
phone BYTEA, -- 密文(信封加密后的手机号)
diagnosis BYTEA, -- 密文(信封加密后的诊断记录)
dek_id_number BYTEA, -- 加密后的 DEK(对应 id_number)
dek_phone BYTEA, -- 加密后的 DEK(对应 phone)
dek_diagnosis BYTEA, -- 加密后的 DEK(对应 diagnosis)
created_at TIMESTAMP DEFAULT NOW()
);7.2 应用层加密的挑战
应用层加密引入了几个工程难题:
查询能力丧失。加密后的字段无法使用 SQL
的
WHERE、ORDER BY、JOIN
等操作。例如,你无法执行
SELECT * FROM patients WHERE phone = '13800138000',因为
phone 列存储的是密文。
解决方案包括:
- 盲索引(Blind Index):对明文计算 HMAC 摘要作为查询索引。HMAC 是确定性的,相同明文产生相同摘要,但无法从摘要反推明文。
import hmac
import hashlib
def create_blind_index(plaintext: str, key: bytes) -> str:
"""生成盲索引用于加密字段的等值查询"""
return hmac.new(key, plaintext.encode('utf-8'), hashlib.sha256).hexdigest()
# 存储时同时写入盲索引
blind_phone = create_blind_index("13800138000", blind_index_key)
# INSERT INTO patients (phone, phone_blind_index, ...) VALUES (encrypted_phone, blind_phone, ...)
# 查询时使用盲索引
target_blind = create_blind_index("13800138000", blind_index_key)
# SELECT * FROM patients WHERE phone_blind_index = target_blind客户端预计算:在应用层维护一个加密搜索索引。适用于数据量不大的场景。
可搜索加密(Searchable Encryption):学术领域的方案,目前工程落地的成熟度有限。MongoDB 的 Queryable Encryption(6.0 版本引入)是一个早期实践。
性能开销。每次读写都需要加解密操作,批量查询时 KMS 调用量会成为瓶颈。优化手段包括 DEK 缓存、批量解密接口、本地 KMS 代理等。
开发复杂度。加密逻辑分散在业务代码中,容易出错。建议封装为统一的加密
SDK 或中间件,业务代码只需调用 encrypt(field)
和 decrypt(field)。
7.3 MongoDB Queryable Encryption
MongoDB 6.0 引入的可查询加密(Queryable Encryption)是应用层加密的一个重要工程突破。它允许在加密字段上执行等值查询和范围查询,同时数据在服务端始终以密文形式存在。
// MongoDB Queryable Encryption 配置示例
const encryptedFieldsMap = {
"medicalRecords.patients": {
fields: [
{
path: "ssn",
bsonType: "string",
queries: { queryType: "equality" }
},
{
path: "billing.amount",
bsonType: "int",
queries: { queryType: "range", min: 0, max: 1000000 }
}
]
}
};
// 使用加密客户端
const encryptedClient = new MongoClient(uri, {
autoEncryption: {
keyVaultNamespace: "encryption.__keyVault",
kmsProviders: { aws: { accessKeyId, secretAccessKey } },
encryptedFieldsMap
}
});
// 查询加密字段(客户端自动处理加解密)
const patient = await encryptedClient
.db("medicalRecords")
.collection("patients")
.findOne({ ssn: "123-45-6789" });底层原理是使用结构化加密(Structured Encryption)方案,在插入时生成额外的加密索引令牌,查询时客户端将查询条件也加密为令牌,服务端通过令牌匹配而不接触明文。
八、同态加密
8.1 同态加密的分类
同态加密(Homomorphic Encryption,HE)允许在密文上直接执行计算,计算结果解密后等同于对明文执行相同计算的结果。这是加密技术的”圣杯”——理论上可以实现数据在整个生命周期中始终保持加密状态。
同态加密分为三类:
部分同态加密(Partially Homomorphic Encryption,PHE):只支持一种运算。RSA 支持乘法同态,Paillier 支持加法同态。PHE 已经有实际应用,例如在电子投票和隐私保护的数据聚合中。
某些同态加密(Somewhat Homomorphic Encryption,SHE):支持有限次数的加法和乘法。次数超过限制后密文噪声会导致解密失败。
全同态加密(Fully Homomorphic Encryption,FHE):支持任意次数的加法和乘法,理论上可以执行任意计算。2009 年 Craig Gentry 首次构造出 FHE 方案,但性能远未达到实用水平。
8.2 FHE 的性能现状
FHE 的性能是其工程落地的最大障碍。以下是 2024 年主流 FHE 库的基准性能:
| 操作 | 明文耗时 | FHE 耗时 | 倍率 |
|---|---|---|---|
| 32 位整数加法 | ~1 ns | ~0.1 ms | 100,000x |
| 32 位整数乘法 | ~1 ns | ~10 ms | 10,000,000x |
| AES-128 计算 | ~0.1 us | ~6 min | 3,600,000,000x |
| 逻辑回归推理(小模型) | ~0.01 ms | ~30 s | 3,000,000x |
即使在专用硬件(GPU 加速、FPGA 加速)上,FHE 的性能开销仍然在 10,000 倍以上。这意味着 FHE 目前不适用于在线事务处理(OLTP)场景。
8.3 FHE 的实际应用场景
尽管性能受限,FHE 在以下场景中已经有实际或接近实际的应用:
隐私保护的机器学习推理:将模型部署在云端,用户提交加密的输入,云端在密文上执行推理,返回加密的结果。用户解密后获得推理结果,云端全程不接触明文。适用于医疗诊断、信用评分等对隐私要求极高的场景。
加密数据库查询:CipherCompute、Zama 等公司提供基于 FHE 的加密数据库查询方案。性能可接受的前提是查询复杂度低(简单的
SUM、COUNT等聚合操作)。多方安全计算(Multi-Party Computation,MPC):FHE 与 MPC 结合,在多个参与方之间执行联合计算而不泄露各方的原始数据。金融风控的联合建模是一个典型场景。
8.4 工程建议
对于大多数团队,当前阶段的务实建议是:
- 在线处理使用传统加密(AES-GCM + 信封加密),不要使用 FHE;
- 离线分析考虑 PHE:如果只需要加法运算(例如加密后的求和统计),Paillier 等 PHE 方案性能可接受;
- 关注 FHE 硬件加速进展:Intel HEXL、DARPA DPRIVE 等项目正在开发 FHE 专用加速器,预计在 2026-2028 年可能达到实用性能。
九、工程案例
9.1 案例背景:某互联网医疗平台的多层加密实践
一家互联网医疗平台(以下称”MedTech”)处理患者的电子健康记录(Electronic Health Record,EHR)。平台需要满足以下合规要求:
- 中国《个人信息保护法》(PIPL):个人敏感信息需加密存储;
- 《健康医疗数据安全指南》:医疗诊断数据需要字段级加密;
- 等保三级:传输加密、存储加密均为必选项。
平台日活用户 200 万,核心数据库存储约 5 亿条诊疗记录,单日新增约 80 万条。
9.2 加密架构设计
MedTech 采用三层叠加的加密架构:
第一层:传输加密。所有外部流量通过 TLS
1.3 接入。微服务之间使用 Istio mTLS。数据库连接使用
sslmode=verify-full。
第二层:静态加密。PostgreSQL 启用 pg_tde 扩展,使用 AES-256 加密数据文件和 WAL 日志。对象存储(MinIO)启用服务端加密(SSE-KMS)。
第三层:应用层加密。对以下字段启用信封加密:
| 字段 | 加密算法 | DEK 粒度 | 盲索引 |
|---|---|---|---|
| 身份证号 | AES-256-GCM | 每条记录独立 DEK | HMAC-SHA256 |
| 手机号 | AES-256-GCM | 每条记录独立 DEK | HMAC-SHA256 |
| 诊断记录 | AES-256-GCM | 每条记录独立 DEK | 不需要查询 |
| 处方详情 | AES-256-GCM | 每条记录独立 DEK | 不需要查询 |
9.3 KMS 部署方案
MedTech 选择自建 HashiCorp Vault 集群作为 KMS,原因包括:
- 数据主权要求——密钥不能托管在公有云 KMS;
- 需要支持混合云部署——部分服务在公有云,部分在自建机房;
- Vault 的 Transit 引擎原生支持信封加密和密钥轮换。
Vault 集群部署架构:
- 3 节点 Raft 共识集群,跨 3 个可用区;
- 后端存储使用 Raft 内置存储(替代 Consul);
- 自动解封(Auto Unseal)通过阿里云 KMS 实现;
- 审计日志实时写入 Kafka,落盘到合规审计系统。
9.4 性能优化
初始方案的性能数据:
| 操作 | 未加密延迟 | 加密后延迟 | 增加比例 |
|---|---|---|---|
| 单条诊疗记录写入 | 2 ms | 12 ms | 500% |
| 单条诊疗记录读取 | 1 ms | 8 ms | 700% |
| 批量查询(100 条) | 15 ms | 450 ms | 2900% |
初始性能不可接受。团队通过以下优化将延迟降低到可接受范围:
优化一:DEK 缓存。在应用内存中缓存解密后的 DEK,缓存 TTL 为 5 分钟。缓存命中后不需要调用 Vault,单条记录的加解密延迟从 8-12 ms 降到 0.5-1 ms。
优化二:批量解密接口。Vault Transit 引擎支持批量操作。将 100 条记录的 100 次 DEK 解密请求合并为一次批量请求,网络开销从 100 RTT 降为 1 RTT。
优化三:Vault Agent 本地代理。在每个应用节点部署 Vault Agent,作为本地缓存代理。进一步减少网络延迟和 Vault 集群压力。
优化后的性能数据:
| 操作 | 未加密延迟 | 优化后延迟 | 增加比例 |
|---|---|---|---|
| 单条记录写入 | 2 ms | 3 ms | 50% |
| 单条记录读取(缓存命中) | 1 ms | 1.5 ms | 50% |
| 批量查询(100 条,缓存命中) | 15 ms | 25 ms | 67% |
| 批量查询(100 条,缓存未命中) | 15 ms | 55 ms | 267% |
最终方案的整体性能开销控制在 50%-70%,对用户体验的影响可接受(P99 延迟仍在 200 ms 以内)。
9.5 经验总结
MedTech 团队在实施过程中获得的关键经验:
先做威胁建模,再选加密方案。不是所有字段都需要应用层加密。患者姓名经过脱敏处理后用 TDE 保护即可,不需要字段级加密。过度加密会显著增加开发和运维成本。
DEK 缓存是性能的关键。没有缓存的信封加密在实际工程中几乎不可用。缓存 TTL 的选择需要平衡安全性和性能——5 分钟是一个经过验证的合理值。
密钥轮换必须自动化。手动轮换密钥在大规模系统中不可行。MedTech 使用 Vault 的自动轮换功能,KEK 每 90 天自动轮换,旧版本保留 2 年用于解密历史数据。
加密 SDK 统一封装。将加密逻辑封装为内部 SDK,业务团队只需要声明哪些字段需要加密,不需要关心具体的加密实现。这大幅降低了出错概率。
十、选型对比
10.1 加密方案对比
| 维度 | TDE(静态加密) | TLS/mTLS(传输加密) | 字段级信封加密(应用层) | FHE(全同态加密) |
|---|---|---|---|---|
| 防护威胁 | 物理介质窃取 | 网络窃听、MITM | DBA 越权、SQL 注入、日志泄露 | 云厂商不可信、全链路保护 |
| 性能开销 | 3%-10% | 1%-3%(TLS 1.3) | 50%-300%(取决于缓存策略) | 10,000x-1,000,000x |
| 应用改造 | 无(透明) | 配置级(证书管理) | 大(需要修改数据模型和查询逻辑) | 极大(需要重写计算逻辑) |
| 密钥管理复杂度 | 低(数据库内置) | 中(证书生命周期) | 高(KMS + DEK 缓存 + 轮换) | 高(密钥尺寸大,管理复杂) |
| 查询能力影响 | 无 | 无 | 严重(需要盲索引等变通方案) | 有限支持 |
| 合规满足度 | 基础合规 | 基础合规 | 高等级合规(PCI DSS、HIPAA) | 最高等级 |
| 工程成熟度 | 成熟 | 成熟 | 成熟 | 实验阶段 |
10.2 KMS 方案对比
| 维度 | AWS KMS | GCP Cloud KMS | HashiCorp Vault | 自建 KMS |
|---|---|---|---|---|
| 部署模式 | 全托管 | 全托管 | 自运维/HCP 托管 | 自运维 |
| HSM 支持 | CloudHSM(FIPS 140-2 Level 3) | Cloud HSM | 外接 HSM 或软件密钥 | 取决于选型 |
| 多云支持 | 仅 AWS | 仅 GCP | 多云、混合云 | 取决于实现 |
| 密钥导入 | 支持 BYOK | 支持 BYOK | 支持 | 支持 |
| 自动轮换 | 支持(年度) | 支持(可配置) | 支持(可配置) | 需自行实现 |
| 定价模型 | 按请求计费 | 按请求计费 | 开源/企业版 | 硬件 + 研发成本 |
| 适用场景 | AWS 单云 | GCP 单云 | 多云、混合云、高安全 | 极端数据主权要求 |
10.3 选型建议
所有系统都应该启用 TDE 和 TLS。这两层加密的性能开销极低,配置简单,是安全基线。不启用这两层没有任何合理理由。
处理敏感个人信息(PII)的系统需要应用层加密。身份证号、银行卡号、医疗记录等字段需要字段级加密。使用信封加密模式,配合 DEK 缓存优化性能。
单云环境优先使用云厂商 KMS。AWS KMS、GCP Cloud KMS 的运维成本远低于自建方案,且与云服务(S3、RDS 等)有原生集成。
多云或混合云环境选择 Vault。Vault 的跨云能力和丰富的密钥引擎使其成为混合云场景的首选。
FHE 目前仅适用于研究和特定离线场景。不要在生产系统的在线链路中使用 FHE,除非你有非常特殊的隐私计算需求且可以接受极高的性能开销。
十一、总结
数据加密不是一个”开关”——它是一个分层的工程体系。三层加密对应三种威胁模型:
- 静态加密防护物理介质泄露,代价最低,应该作为所有系统的默认配置;
- 传输加密防护网络窃听,TLS 1.3 + mTLS 是零信任架构的基础设施;
- 应用层加密防护内部人员和应用层漏洞,是合规要求最高的那一层,也是工程代价最大的一层。
信封加密是应用层加密的核心模式。两层密钥架构(DEK + KEK)在安全性、性能和运维成本之间取得了最好的平衡。KMS 作为密钥层级的管理中心,其高可用性和密钥轮换自动化是整个加密架构可靠运行的前提。
同态加密代表了加密技术的终极目标——数据在全生命周期中保持加密状态。但目前的性能瓶颈使其仍处于工程试验阶段。关注 FHE 硬件加速的进展,但不要在当前的生产系统中押注。
密钥管理是加密架构中最容易出错的环节。密钥的生成、存储、轮换、吊销需要严格的流程和自动化工具。关于密钥和证书管理的详细讨论,参见下一篇密钥与证书管理。
参考资料
- NIST SP 800-57 Part 1: Recommendation for Key Management. NIST, 2020.
- NIST SP 800-111: Guide to Storage Encryption Technologies for End User Devices. NIST, 2007.
- AWS Well-Architected Framework - Security Pillar: Data Protection. Amazon Web Services.
- HashiCorp Vault Transit Secrets Engine Documentation. HashiCorp.
- Gentry, C. “A Fully Homomorphic Encryption Scheme.” Stanford University, 2009.
- Boneh, D., et al. “Applied Cryptography: Protocols, Algorithms and Source Code in C.” Wiley, 2018.
- PostgreSQL pg_tde Extension Documentation. Percona.
- Microsoft SQL Server Transparent Data Encryption (TDE) Documentation. Microsoft.
- MongoDB Queryable Encryption Documentation. MongoDB Inc.
- Google Infrastructure Security Design Overview: Encryption in Transit. Google Cloud.
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3. IETF, 2018.
- PCI DSS v4.0: Requirement 3 - Protect Stored Account Data. PCI Security Standards Council, 2022.
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【系统架构设计百科】架构质量属性:不只是"高可用高性能"
需求评审时写下的'高可用、高性能、高并发',到了架构设计阶段几乎无法落地——因为它们不是可执行的需求。本文从 SEI/CMU 的质量属性理论出发,用 stimulus-response 场景模型把模糊需求变成可量化、可验证的架构约束,并拆解属性之间的冲突与联动关系。
【系统架构设计百科】告警策略:如何避免"狼来了"
大多数团队的告警系统都在制造噪声而不是传递信号。阈值告警看似直观,实则产生大量误报和漏报,值班工程师在凌晨三点被叫醒,却发现只是一次无害的毛刺。本文从告警疲劳的工业数据出发,拆解基于 SLO 的多窗口燃烧率告警算法,深入 Alertmanager 的路由、抑制与分组机制,结合 PagerDuty 的告警疲劳研究和真实工程案例,给出一套可落地的告警策略设计方法。
【系统架构设计百科】复杂性管理:架构的核心战场
系统复杂性是架构腐化的根源——本文从 Brooks 的本质复杂性与偶然复杂性划分出发,结合认知负荷理论与 Parnas 的信息隐藏原则,系统阐述复杂性的来源、度量与控制手段,并给出可操作的架构策略
【系统架构设计百科】微服务架构深度审视:优势、代价与适用边界
微服务不是免费的午餐。本文从分布式系统八大谬误出发,拆解微服务真正解决的问题与引入的代价,梳理服务边界划分的工程方法论,还原 Amazon 和 Netflix 从单体到微服务的真实演进时间线,给出微服务适用与不适用的判断框架。