4、安全启动链:从BootROM到应用层的逐级验证

安全启动链,说白了就是给设备的每一层软件都贴上一张「防伪标签」。从芯片上电的第一条指令开始,逐级检查,确保你跑的都是官方正版固件。我在做智能门锁项目时,就见过跳过这步的设备——被攻击者刷了个恶意固件,远程开锁,整栋楼的门禁都成了摆设。

4.1 信任锚点:BootROM 为什么不可篡改?

整个安全启动链的根,是芯片内部固化的 BootROM。这段代码在芯片流片时就写死了,物理上无法修改。为什么非要这么设计?

你想想看,如果信任根放在 Flash 里,攻击者只要把 Flash 拆下来用编程器一读一写,就能替换掉。BootROM 在芯片内部,没有外部总线接口,想改?除非你用聚焦离子束(FIB)去切芯片内部金属层——这成本够买几百台设备了。

BootROM 的核心职责就两件事:

  • 初始化硬件:时钟、内存控制器、安全引擎
  • 验证下一级镜像:通常是 Bootloader 或 Flash 中的第一个可执行程序

我在项目中遇到过一种情况:某厂商为了省成本,把 BootROM 的一部分功能放到了外部 SPI Flash 里。结果呢?攻击者用电压故障注入让 BootROM 跳过验证,直接执行外部代码。嗯,这里要注意——信任根必须物理不可变,任何「折中方案」都是在给攻击者留后门。

4.2 信任链传递:一级签一级,环环相扣

安全启动不是一次验证就完事,而是链式的。每一级在运行前,都要验证下一级的签名和哈希。我把这个流程画成了图,方便你理解。

核心原则:每一级只信任上一级,上一级只验证下一级的完整性。任何一级验证失败,系统立即停止启动。

安全启动信任链传递流程 BootROM(信任根) Bootloader 系统内核(OS) 应用层(App) 验证 Bootloader 签名+哈希 公钥固化在 BootROM 中 验证内核签名+哈希 公钥由 Bootloader 传递 验证应用层签名+哈希 公钥由内核传递 应用层运行 信任链建立完成

每一级验证时,实际做的是两件事:

  1. 哈希校验:计算下一级镜像的哈希值,与签名中携带的哈希比对
  2. 签名验证:用上一级的公钥解密签名,确认哈希没有被篡改

公钥怎么来的?BootROM 里固化了根公钥的哈希。Bootloader 加载时,BootROM 用这个哈希验证 Bootloader 携带的公钥是否合法。之后 Bootloader 再用自己的私钥签名内核,内核签名应用。每一级的公钥都由上一级担保,这就是信任链的传递。

4.3 度量机制:不只是验证,还要记录

安全启动还有一个重要概念叫「度量」(Measurement)。验证通过后,系统会把当前层的哈希值扩展到平台配置寄存器(PCR)中。这有什么用?

举个例子。我做过一个医疗设备项目,法规要求每次启动都要记录「谁启动了、启动了什么版本」。度量机制正好解决这个问题——PCR 的值会随着启动过程不断累加,最终形成一个唯一的系统指纹。远程证明时,服务器只要比对 PCR 值,就知道设备当前运行的是不是合法固件。

启动阶段 度量内容 PCR 扩展值
BootROM Bootloader 镜像哈希 PCR[0] = SHA256(BootROM || Bootloader)
Bootloader 内核镜像哈希 PCR[1] = SHA256(PCR[0] || Kernel)
内核 应用层镜像哈希 PCR[2] = SHA256(PCR[1] || App)

注意,PCR 只能扩展不能重置(除非硬件复位)。这意味着攻击者即使绕过了某一层的验证,PCR 的值也会「暴露」他——因为扩展后的值跟预期对不上。

4.4 回滚攻击:版本号机制是最后一道防线

验证签名和哈希就够了吗?不够。还有一种经典的攻击方式叫「回滚攻击」。攻击者不篡改固件,而是把设备刷回一个有漏洞的旧版本。旧版本的签名是合法的,哈希也对得上,但漏洞已经被公开了。

我在一个智能摄像头项目里就遇到过。攻击者把固件从 v2.1 回滚到 v1.0,v1.0 的固件签名是合法的,但有一个缓冲区溢出漏洞。攻击者利用这个漏洞拿到了 root 权限。怎么防?

版本号机制:每个固件镜像的头部必须包含一个单调递增的版本号。Bootloader 在验证固件前,先检查版本号是否大于等于当前运行的版本。如果发现版本号更低,直接拒绝启动。

具体实现上,版本号通常存储在一次性可编程(OTP)区域或防回滚计数器(RPMB)中。每次升级成功,计数器就加一。攻击者想回滚?除非他能物理篡改 OTP——这比改 BootROM 还难。

我曾经见过一个厂商把版本号存在普通 Flash 里,结果攻击者直接替换了 Flash 内容,版本号也跟着「回滚」了。嗯,这里要注意——版本号的存储介质必须防篡改,OTP 或安全元件(SE)是首选。

4.5 避坑指南:我踩过的几个坑

  • 公钥硬编码别用裸公钥:我早期一个项目直接把 RSA 公钥的二进制硬编码在 BootROM 里。后来发现,攻击者可以通过差分功耗分析(DPA)把公钥提取出来。正确的做法是只存公钥的哈希,公钥本身由 Bootloader 携带。
  • 验证失败别直接死机:有些设备验证失败就无限循环,连调试串口都不给。我建议至少留一个恢复模式——按住某个按键启动,跳过验证,允许刷入官方固件。否则设备变砖,用户只能返厂。
  • 别忘了验证签名算法本身:我记得有个案例,攻击者把签名算法从 SHA256-RSA2048 替换成了 SHA1-RSA1024,然后暴力碰撞 SHA1 哈希。BootROM 只检查「签名通过」,没检查算法强度。所以,签名算法也要固化在信任根里。

个人习惯:我在设计安全启动链时,会在 BootROM 里预留一个「紧急回退」公钥的哈希。这个公钥只有厂商持有,用于极端情况下的设备恢复。平时不用,但真遇到 Bootloader 损坏,至少还有一条活路。

安全启动链不是银弹,但它把攻击门槛从「写个脚本就能黑」提高到了「需要物理接触+专业设备」。对于物联网设备来说,这层防护已经能挡住 90% 以上的常见攻击。剩下的 10%,需要结合安全更新、运行时防护和远程证明来补全——这些我们后面会聊到。