2、TDE基本原理

这一章我打算把TDE最核心的四个东西讲透:数据文件怎么加密的、日志文件为什么也要加密、内存里数据是怎么解密的、以及那个让人头疼的密钥层级关系。

说白了,TDE就是“透明”地帮你把落盘的数据糊上一层密文。你应用层根本不用改代码,数据库自己搞定。

数据文件加密:页面级透明加密

在SQL Server里,TDE加密的粒度是页面(8KB),而不是行或列。数据写到磁盘前,整页会被加密;读回内存时,自动解密。这个过程完全在数据页的I/O路径上发生,对上层逻辑透明。

我的经验:之前有个项目,老板非要对比TDE开启前后的TPS。我拿同一个压测脚本跑了一遍,发现写入吞吐量掉了约3%~5%,读取几乎没差别。为什么?因为写入需要多一次加密计算,而读取时数据页很可能已经在缓冲池里是解密状态了。

具体加密流程:

  1. 修改后的脏页准备写回磁盘。
  2. 数据库引擎检查该数据页所属的数据库是否启用了TDE。
  3. 从内存中获取该数据库对应的数据库加密密钥(DEK)。
  4. 对整页(Page Header保留不加密,因为需要做校验)进行AES-256加密。
  5. 加密后的页面被写入数据文件。

注意:Page Header本身不加密,只加密数据行和槽位。否则数据库连这个页是谁的都搞不清楚。

日志文件加密:redo/undo日志的加密

很多人以为只加密数据文件就够了,这其实是个大坑。你想想看,事务的变更都记在日志里,如果日志文件明文,那把日志文件拷走照样能恢复出敏感数据。

曾经踩过的坑:有一次我在一台测试机上只对数据文件启用了TDE,忘了日志文件。事后审计发现攻击者通过日志备份还原了部分表数据。打那以后,我每次部署TDE都会特意检查日志文件是否也被加密了。

日志加密的原理差不多,但有个关键区别:日志记录的加密是同步的。每一条日志写入前都要在线加密,因此对整体写负载影响更明显。不过好在日志是顺序写的,性能损失其实可控。

TDE加密的日志包括:

  • 活动事务日志(Active Log)
  • 已归档的日志备份文件
  • tempdb的日志(如果tempdb也开启了TDE)

嗯,这里要注意:tempdb的TDE不是必选的,但我建议你开启。因为临时表、排序、索引重建都可能涉及敏感数据。

内存解密流程:从缓冲区到查询引擎

数据页从磁盘读到内存,加密状态进缓冲池?不,实际上在读入之前就解密了。流程如下:

  1. I/O子系统请求读取某个数据页。
  2. 磁盘控制器返回加密后的页面。
  3. 数据库引擎在放入缓冲池之前,使用DEK对该页进行解密。
  4. 解密后的明文页被放进缓冲池,然后才被查询引擎使用。
关键点:缓冲池里存的是明文。所以如果有人能直接dump内存(比如通过OS内存转储),那TDE就防不住了。这也是为什么我总强调“磁盘加密”和“内存安全”要一起抓。

你可能会问:如果内存明文,那TDE还有什么用?

答案:TDE解决的是静态数据泄露风险——比如磁盘被盗、备份文件泄露、存储介质退役。它不解决运行时内存攻击。各司其职。

加密层次结构:证书、密钥、谁保护谁

这是TDE最绕的地方。我画了张图,你先看一眼。

TDE密钥层次结构图 服务主密钥 (SMK) 数据库主密钥 (DMK) 证书 (Certificate) 数据库加密密钥 (DEK) 加密后的数据文件 加密后的日志文件 关键说明 · SMK 由 Windows 数据保护 API (DPAPI) 保护,是整个层级的最根。 · DEK 直接用于加密/解密数据页和日志页,密钥长度 256 位。 · 备份证书和私钥!否则一旦主密钥损坏,数据永不可读。

这张图里,从上到下依次是:

  • 服务主密钥(SMK):由Windows系统保护,每个实例一个。
  • 数据库主密钥(DMK):由SMK加密存储,可自定义密码。
  • 证书:由DMK加密,用于保护DEK。
  • 数据库加密密钥(DEK):被证书加密后存储在数据库启动记录中。实际干活的就是它。

我习惯把这个层次看作“套娃保护”——每一层密钥只保护下一层的密钥,最终只有DEK碰实际数据。这样即使攻击者拿到了数据文件,没有整个链路根本解不开。

表格:四种密钥的对比

密钥 存储位置 作用范围 谁保护它 备份重要性
SMK 操作系统 整个实例 DPAPI 自动备份(依赖OS)
DMK master 数据库 单个数据库 SMK + 密码 强烈建议备份
证书 master 数据库 单个数据库 DMK 必须备份私钥
DEK 数据库启动记录 该库所有数据页/日志 证书(或EKM) 由证书间接保护

最后提一句:上面这些密钥的备份恢复,是TDE避坑指南里的重中之重。我曾经帮一个客户恢复数据库,结果他们丢了证书的私钥……最后只能从冷备份里翻。那场面,啧,不想经历第二次。