第3章 透明数据加密(TDE)原理

说实话,TDE这个功能刚出来的时候,我第一反应是:这不就是表级加密的升级版吗?后来踩过坑才明白,它俩压根不是一回事。TDE是在存储层面做的加密,对上层应用完全透明。你想想看,应用连SQL都不用改,数据库自己就把数据文件、日志文件、备份文件全给加密了。

我最早接触TDE是在一个金融项目里,客户要求"数据静止"时必须加密。那时候我还在想:要不要用应用层加密?结果一算性能损耗,得了,还是老老实实用TDE吧。

3.1 TDE到底加密了什么?

TDE不是加密某一列,也不是加密某个表。它加密的是整个数据库的物理文件:

  • 数据文件(.mdf, .ndf)——页级加密,写盘时加密,读盘时解密
  • 事务日志文件(.ldf)——日志记录也会被加密
  • 备份文件(.bak)——备份时自动加密,恢复时需要证书
  • tempdb——如果你在加密数据库里创建了临时表,tempdb也会自动加密

TDE不加密内存中的数据,也不加密网络传输。它只保护"静止"状态的数据。数据一旦被读入缓冲池,就是明文。

“那内存中的数据怎么办?”嗯,你需要配合(别急,后面章节会讲)列级加密或者Always Encrypted。TDE只管落盘那一层。

3.2 密钥层次结构——三层保护

这是TDE最核心的设计。我见过很多DBA把TDE证书随便备份到和数据库同一个磁盘上,结果数据库坏了,证书也丢了。这属于典型的设计级失误。

TDE用了三层密钥结构,每一层保护下一层:

层级 名称 存储位置 作用
1 数据库主密钥 (DMK) master数据库 保护下一层证书
2 实例级证书或EKM密钥 master数据库 保护DEK
3 数据库加密密钥 (DEK) 用户数据库 实际加密数据页

你可能会问:为什么要搞三层?直接在数据库里存一个密钥不就行了?

答案很简单——安全性和可恢复性。如果DEK被攻击者拿到,没有上层证书也解不开。如果证书丢了,但你有密码备份的DMK,也可以恢复。我曾经在一个客户现场遇到过证书过期、备份文件也被误删的情况,幸亏DMK的密码还记得,折腾了一上午才把数据救回来。嗯,从那以后我要求所有TDE项目的密钥备份都必须离线上锁保管。

重要:DEK是自动创建和管理的,你不需要手动操作。但你必须备份实例级证书(或EKM密钥)和DMK的密码。没有证书,加密数据库就无法附加或恢复。

来看看TDE密钥层次的实际SQL创建过程:

-- 第一步:创建数据库主密钥(在master数据库)
USE master;
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MyStr0ngP@ssw0rd!';

-- 第二步:创建证书(由主密钥保护)
CREATE CERTIFICATE MyTDECert WITH SUBJECT = 'TDE Certificate';

-- 第三步:备份证书和私钥(这一步不能省!)
BACKUP CERTIFICATE MyTDECert TO FILE = 'D:\Backup\MyTDECert.cer'
   WITH PRIVATE KEY (
      FILE = 'D:\Backup\MyTDECert.pvk',
      ENCRYPTION BY PASSWORD = 'AnotherStrongP@ss'
   );

-- 第四步:创建数据库加密密钥(在需要加密的数据库中)
USE YourDatabase;
CREATE DATABASE ENCRYPTION KEY
   WITH ALGORITHM = AES_256
   ENCRYPTION BY SERVER CERTIFICATE MyTDECert;

-- 第五步:开启加密
ALTER DATABASE YourDatabase SET ENCRYPTION ON;

小技巧:我习惯把证书密码和DMK密码分开存,各放在不同的保险柜里。这样一个人拿到两个密码的概率就极低了。

3.3 页级加密的细节

TDE不是在SQL语句层面加密,而是在数据库页(Page)层面。

每个页面是8KB,TDE对页面进行如下操作:

  1. 写盘:在页面写入磁盘之前,先用DEK对整个页面(含页头)进行加密,然后写入。页头的checksum也会被重新计算并加密。
  2. 读盘:从磁盘读取页面后,先解密再放入缓冲池。应用的查询完全无感知。
  3. 日志:日志记录在写入日志文件前也被加密。但注意:日志备份恢复到其他实例时,也必须提供相同的证书。

“加密后页面大小会变吗?”不会。TDE是原地加密,页面大小还是8KB,只是内容变成了密文。

我曾经帮一家医院排查过TDE导致备份变慢的问题。原因是他们开了TDE后又开了页面压缩。加密和压缩的顺序会互相影响——我建议先压缩后加密,或者干脆只加密不压缩。具体后面故障诊断章节再展开。

TDE 密钥层次结构与数据页加密流程 数据库主密钥 (DMK) 存储在 master 数据库 实例级证书 / EKM 由 DMK 加密保护 数据库加密密钥 (DEK) 存储在用户数据库中 加密保护 加密保护 应用写入数据 缓存中的明文页 TDE 引擎 使用 DEK 加密整个页 写入磁盘 .mdf / .ldf DEK 提供加密密钥 读操作流程相反:磁盘 → TDE引擎解密 → 缓冲池明文页,全程对应用透明,无需修改任何SQL

3.4 对性能的影响——避坑指南

TDE不是免费的。加密和解密都需要CPU资源。我做过一个压测,在一般配置的服务器上,TDE会导致OLTP写入性能下降约3%~5%,读取下降约2%~4%。如果是数据仓库那种大批量扫描,下降会更明显。

  • CPU瓶颈的服务器——TDE会把CPU利用率拉高10%~20%。建议预留足够的CPU余量。
  • 加密算法的选择——AES_128比AES_256快一些,但安全性低一级。金融客户一般强制AES_256。我建议非极端敏感场景用AES_128,性能更优。
  • 开启加密的过程——ALTER DATABASE SET ENCRYPTION ON 是一个后台扫描操作,它会逐页加密整个数据库。期间数据库仍然在线,但日志增长会比较猛。我曾经在生产环境开启TDE,忘记监控日志空间,结果日志文件撑爆了磁盘。嗯,别忘了提前配好日志自动增长和监控。

坑:如果在加密过程中重启实例,下次启动后会继续加密,但之前已加密的页不会丢失。但如果你当时正在加密到一半时强制关机,可能导致一些页处于加密待定状态,重启后自动恢复。我遇到过少数情况需要手动检查sys.dm_database_encryption_keys来确认加密状态。

3.5 审计与监控要点

TDE是否真的在运行?有没有被意外暂停?你需要常看这几个地方:

视图/DMV 查看内容 我的经验
sys.dm_database_encryption_keys 每个数据库的加密状态、DEK指纹、加密百分比 如果encryption_state = 3表示加密完成;= 2表示正在进行
sys.certificates 证书是否存在、过期日期 证书过期后TDE仍然正常工作,但无法创建新加密库
错误日志 TDE相关的错误和警告 常见错误:密码错误、证书找不到、DEK损坏

“TDE加密后我怎么判断数据文件真的加密了?”用十六进制编辑器打开.mdf文件,如果前几KB全是乱码,说明加密了。当然,更正规的做法是查询DMV:

SELECT 
    db_name(database_id) AS DatabaseName,
    encryption_state,
    encryption_state_desc = 
        CASE encryption_state
            WHEN 0 THEN 'No encryption'
            WHEN 1 THEN 'Unencrypted'
            WHEN 2 THEN 'Encryption in progress'
            WHEN 3 THEN 'Encrypted'
            WHEN 4 THEN 'Key change in progress'
            WHEN 5 THEN 'Decryption in progress'
        END,
    percent_complete,
    key_algorithm,
    key_length
FROM sys.dm_database_encryption_keys;

结果一看,一目了然。我记得有一次帮客户排查,发现有个数据库encryption_state一直显示2,进度卡在99%很久不动。最后发现是日志文件空间满了,延长了加密扫描的时间。清理日志后立刻完成。

总结一下:TDE的核心思路就是“加密物理文件,透明给上层”。你只要管好密钥和证书,剩下的交给数据库引擎。但别忘了——证书备份就是你的命根子。没有它,加密数据库就是一堆废铁。我吃过这个亏,希望你不用再吃。