全盘加密应对的威胁模型、分层设计与实践

全盘加密应对的威胁模型、分层设计与实践

💡 原文中文,约6900字,阅读约需17分钟。
📝

内容提要

本文讨论全盘加密的威胁模型与实践,重点是防御离线攻击者而非在线攻击。建议机械硬盘先用随机数填满再创建GELI容器,以隐藏容量并避免已知明文;闪存缓存盘则先执行TRIM。作者主张使用GELI块设备加密而非ZFS原生加密,因为后者会泄露元数据、容易配置失误且实现复杂。完整性校验交由ZFS的Merkle树完成,不启用GELI的HMAC。

🔎

延伸解读

威胁模型决定加密边界

文章明确全盘加密只针对离线攻击者,即拿到物理介质但未获得系统运行时内存的人。一旦系统已引导并挂载解密卷,主密钥常驻内核内存,此时加密不再提供额外保护,防护责任转交操作系统权限与隔离机制。挂起或锁屏通电状态下的冷启动攻击与DMA攻击也超出其防御范围。读者应先确认自身场景是否属于离线威胁,再决定是否投入全盘加密,避免在在线攻击面上产生虚假安全感。

随机填充与TRIM的取舍逻辑

机械硬盘先写满密码学随机数再建GELI容器,可让空闲扇区与已用密文在统计上不可区分,同时避免向加密设备写全0所形成的大面积已知明文样本。代价是闪存盘若保留TRIM,控制器可能将回收块映射为全0,破坏随机背景,因此geli提供-T参数禁止透传TRIM。作者对缓存盘采取相反策略:先全盘TRIM再建容器,因为缓存盘频繁写入且多数时间接近满盘,隐藏容量的收益有限。

块设备加密优于ZFS原生加密

ZFS原生加密以dataset为粒度,为支持未加载密钥时的import、status和scrub,必须将池拓扑、dataset与快照名称、占用字节数、文件大小与空洞分布等元数据保持明文,离线攻击者可据此推断大量敏感上下文。混合加密与未加密dataset还容易因挂载点写错造成静默泄露。GELI工作在块设备层,未解锁时整盘呈现无结构随机数据,安全边界与物理硬盘一致,且与ZFS的快照、克隆等逻辑解耦,减少实现耦合带来的故障面。

完整性校验交给ZFS而非GELI

AES-XTS只提供机密性,不检测密文篡改。GELI的HMAC/SHA256会为每个逻辑扇区额外占用空间,4KiB扇区下约损失11%容量,在4Kn硬盘上开销可达50%,并破坏扇区对齐、引发读改写放大。ZFS的Merkle树将校验和存放在父节点块指针中,整棵树位于GELI容器内,篡改密文解密后必然校验失败并触发ECKSUM,有冗余时还能自愈。但整盘镜像回滚仍无法被ZFS发现,需依赖外部记录的txg或快照列表。

❓

Q&A

全盘加密主要防御什么样的攻击者?

全盘加密主要防御离线攻击者,即拿到包含数据的硬件但未获得系统运行时访问权限的人,例如硬盘返厂维修、介质退役或关机状态下失窃。它不防御在线攻击,系统运行且卷已挂载时,主密钥在内核内存中,若系统被攻破则全盘加密不提供额外保护。

为什么机械硬盘在创建GELI容器前要先填满随机数?

有两个原因:一是消除已用容量侧信道,未分配扇区与已写入密文统计特征一致,攻击者无法判断实际数据量;二是避免构造大面积已知明文,若用抹零方式,空闲区域会成为大量P=0的已知明文样本,预先填充随机数则与GELI密钥体系正交,避免这种关联。

闪存缓存盘为什么采用先TRIM再初始化,而不是填充随机数?

因为缓存盘频繁写入且大部分时间处于满状态,关闭TRIM透传和提前填充随机数价值不高。先执行一次全盘TRIM可重置FTL状态,然后直接建立GELI容器,不预先填充随机数。

为什么作者推荐用GELI块设备加密而不是ZFS原生加密?

ZFS原生加密只加密用户数据,池和数据集元数据(如拓扑、dataset名称、快照名、占用量、文件大小等)保持明文,会泄露信息;且以dataset为粒度,同池可存在未加密dataset,易因配置失误导致静默泄露;此外加密与快照、克隆等逻辑耦合,曾出现棘手Bug。GELI在块设备层加密整盘,未解锁时呈现无结构随机数据,安全边界与物理硬盘一致,且两层解耦更简单可靠。

使用GELI加密时为什么不需要开启HMAC完整性校验?

因为上层ZFS已提供端到端完整性校验(基于Merkle树,校验和存放在父节点块指针中)。GELI的HMAC会为每个扇区额外存储MAC,消耗约11%容量(4Kn盘可达50%),并破坏扇区对齐导致读写放大。而ZFS over GELI下,篡改密文会被ZFS校验捕获并可能自愈,因此无需GELI的HMAC。

在FreeBSD下为机械数据盘建立GELI加密容器的基本步骤是什么?

先用dd if=/dev/random of=/dev/da0 bs=1m在裸盘上填充密码学安全随机数,然后执行geli init -s 4096 -T /dev/da0初始化容器(-T禁止TRIM透传),再geli attach /dev/da0挂载。之后可将/dev/da0.eli交给ZFS创建存储池,并设置checksum=blake3,不开启ZFS原生加密。

🏷️

标签

➡️

继续阅读