2、信任链基础:信任根、信任链传递机制、度量与验证模型
聊可信启动,绕不开一个核心问题:凭什么相信一个系统是“干净”的?
你想想看,从按下电源键到操作系统跑起来,中间经历了固件、引导程序、内核加载……任何一个环节被篡改,整个系统就沦陷了。所以,我们需要一条信任链,从最底层开始,一级一级往上验证。
2.1 信任根(Root of Trust)
信任根,说白了就是整个信任链的起点。它必须是一个绝对可信的实体,不能被篡改,也不能被绕过。
我个人习惯把信任根分成两类:
- 硬件信任根(HRoT):比如 TPM 芯片、Intel PTT、ARM TrustZone 中的安全元件。这些硬件在出厂时就被植入了不可更改的密钥和固件。
- 固件信任根(FRoT):比如 UEFI 安全启动中的平台密钥(PK)。它存储在 SPI Flash 中,理论上可以被物理改写,但通常有写保护机制。
核心原则:信任根必须满足三个条件——不可伪造、不可篡改、不可绕过。缺一个,信任链就断了。
我在项目中遇到过一件事:某厂商为了省成本,把信任根密钥直接硬编码在 BootROM 里,结果被逆向工程提取出来,整个产品线的安全防线瞬间崩塌。嗯,这就是典型的“信任根不信任”案例。
2.2 信任链传递机制
有了信任根,接下来就是怎么把信任传递下去。这个过程,我习惯叫它“接力赛”。
每一棒(下一级组件)在起跑前,必须先被上一棒验证。验证通过,才把控制权交出去。这就是信任链传递的核心逻辑。
典型的信任链传递流程如下:
- 第一棒:BootROM(固化在芯片内部,不可修改)
- 第二棒:Bootloader(如 U-Boot SPL)
- 第三棒:主引导程序(如 U-Boot proper 或 GRUB)
- 第四棒:操作系统内核(如 Linux Kernel)
- 第五棒:内核模块与关键服务
每一棒在启动下一棒之前,都会做两件事:
- 度量:计算下一棒代码的哈希值
- 验证:用上一棒的公钥验证这个哈希值的签名
避坑指南:我曾经遇到一个项目,BootROM 验证了 Bootloader 的签名,但 Bootloader 在加载内核时,竟然没有验证内核的签名。结果攻击者替换了内核,系统照样启动。记住:信任链不能断,每一棒都必须验证下一棒。
2.3 度量与验证模型
度量与验证,是信任链的“安检员”。它们负责检查每一棒是否被篡改过。
2.3.1 度量(Measurement)
度量就是计算组件的哈希值。常用的算法有 SHA-256、SHA-384 等。度量结果通常存储在 TPM 的 PCR(平台配置寄存器)中。
举个例子,度量一个内核镜像:
# 计算内核镜像的 SHA-256 哈希值
sha256sum vmlinuz-5.10.0
# 输出示例
# a1b2c3d4e5f6... vmlinuz-5.10.0
这个哈希值会被记录到 PCR 中。注意,PCR 的值是累加的——每次度量新组件,都会和之前的 PCR 值一起做一次哈希扩展。这样,最终 PCR 值就代表了整个信任链的“指纹”。
2.3.2 验证(Verification)
验证就是拿度量结果和预期值做比较。预期值通常是一个签名过的哈希值,存储在安全区域(比如 TPM 的 NVRAM 或固件分区中)。
验证流程大致如下:
- 用公钥解密签名,得到预期的哈希值
- 比较实际度量的哈希值和预期值
- 如果一致,验证通过;否则,拒绝启动
注意:公钥本身也必须被保护。如果攻击者替换了公钥,那签名验证就形同虚设。所以,公钥通常存储在信任根中,或者由信任根来验证公钥的完整性。
2.3.3 两种验证模型
在实际部署中,我见过两种主流的验证模型:
| 模型 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 静态信任链 | 在启动时一次性完成所有度量与验证 | 实现简单,性能开销小 | 无法应对运行时攻击 |
| 动态信任链 | 在系统运行过程中持续度量与验证 | 能检测运行时篡改 | 实现复杂,性能开销大 |
我个人更倾向于动态信任链,尤其是对安全性要求高的场景。但要注意,动态验证会引入额外的性能开销。我曾经在一个嵌入式项目中,因为动态验证频率设置得太高,导致系统响应延迟增加了 30%。后来调整了验证策略,才平衡了安全与性能。
2.4 信任链的图形化理解
为了让你更直观地理解信任链的传递过程,我画了一张图:
2.5 避坑总结
最后,我把这些年踩过的坑总结一下:
- 信任根不能“假”:别把信任根密钥放在可读写的存储介质里。我曾经见过有人把 PK 放在 SPI Flash 的未保护区域,结果被轻松改写。
- 信任链不能“断”:每一棒都必须验证下一棒。别偷懒,别跳过任何一环。
- 度量不能“少”:不仅要度量代码本身,还要度量配置数据、环境变量等。攻击者经常在这些“边角料”里动手脚。
- 验证不能“慢”:动态验证的频率要合理。太频繁影响性能,太稀疏又失去意义。我一般建议在关键操作(如加载模块、执行脚本)时触发验证。
一句话总结:信任链的本质,就是用不可篡改的根,通过逐级验证,确保整个启动过程可度量、可验证、可追溯。
嗯,信任链基础就聊到这里。记住这些原则,后面讲具体部署时,你会少踩很多坑。