3、安全启动链:BootROM、Bootloader、签名验证、回滚保护

安全启动链,说白了就是给设备上电后的第一道防线。我经常跟团队说,如果这层没守住,后面做再多加密都是白搭。你想想看,攻击者要是能在系统启动阶段注入恶意代码,那整个设备就等于拱手让人了。

这一章,咱们就聊聊启动链上的四个关键环节:BootROM、Bootloader、签名验证,还有回滚保护。每个环节我都踩过坑,咱们边聊边看。

3.1 BootROM:信任的起点

BootROM 是芯片出厂时固化在 ROM 里的一段代码。它不可写,不可改。这就是整个信任链的根。

我个人习惯把 BootROM 比作「保险箱的锁芯」。锁芯坏了,整个保险箱就没意义了。BootROM 的任务很简单:上电后,验证下一级 Bootloader 的签名。验证通过,才把控制权交出去。

核心要点:BootROM 必须做到「只读、不可篡改、不可绕过」。这是安全启动的基石。

我在项目中遇到过一件事:某款芯片的 BootROM 有个硬件 bug,导致攻击者可以通过电压毛刺攻击跳过签名验证。嗯,那一次我们花了整整两周才定位到问题。最后只能通过芯片改版修复,代价非常大。

3.2 Bootloader:分阶段加载

Bootloader 通常分两个阶段:

  • 第一阶段(SPL / Pre-loader):由 BootROM 加载,负责初始化 DDR、时钟等基础硬件。代码量极小,通常只有几 KB。
  • 第二阶段(U-Boot / Little Kernel):由第一阶段加载,负责加载操作系统内核。功能更丰富,支持文件系统、网络等。

为什么要分阶段?说白了,BootROM 太小了,装不下复杂逻辑。而且早期硬件还没初始化,你连内存都用不了,怎么跑大代码?

我建议你在设计时,把第一阶段做得越简单越好。代码越少,攻击面越小。我曾经见过一个项目,把文件系统解析逻辑塞进了第一阶段,结果被爆出缓冲区溢出漏洞。教训深刻啊。

3.3 签名验证:谁来签?怎么验?

签名验证是安全启动的核心。流程大致如下:

  1. 镜像发布前,用私钥对镜像进行签名,生成签名值。
  2. 设备端 BootROM 或 Bootloader 用公钥验证签名。
  3. 验证通过,加载镜像;验证失败,拒绝启动。

这里有个关键问题:公钥放哪?

存储位置 安全性 灵活性 我见过的坑
芯片 OTP(一次性可编程) 低(不可更改) OTP 烧录后才发现公钥错了,整批芯片报废
BootROM 代码中 容易被逆向工程提取
外部存储(如 eFuse) 中(可熔断多次) 熔断时序没控制好,导致公钥损坏

我个人更推荐用 OTP 或 eFuse。虽然灵活性差了点,但安全性有保障。你想想看,公钥要是能被替换,那签名验证还有什么意义?

实战技巧:签名算法建议用 ECDSA(椭圆曲线数字签名算法),比 RSA 效率高,密钥更短。我在平板项目上用过 ECDSA P-256,验证一次不到 10 毫秒。

3.4 回滚保护:防降级攻击

回滚保护,就是防止攻击者把系统降级到有漏洞的旧版本。举个例子:

你的设备当前跑的是 v3.0 固件,修复了某个高危漏洞。攻击者拿到 v2.0 的旧镜像,想刷回去利用已知漏洞。如果没有回滚保护,设备会乖乖启动旧版本。这就是降级攻击。

回滚保护的常见做法:

  • 版本号计数器:在 OTP 或 eFuse 中维护一个单调递增的版本号。每次升级,版本号必须大于当前值。
  • 防回滚标志位:熔断 eFuse 中的特定位,标记某个版本已过时。
  • 安全存储:将版本号写入安全存储区,防止篡改。

我曾经在项目中踩过一个坑:版本号计数器用的是普通 Flash 存储,结果攻击者通过物理手段直接修改了 Flash 内容,把版本号改回了 0。嗯,从那以后,我坚持把版本号放在 OTP 里。虽然成本高一点,但安全多了。

注意:回滚保护一旦启用,就很难撤销。如果你在开发阶段频繁刷机,建议先关闭回滚保护。等量产时再开启。我见过有团队在开发板上烧了 OTP,结果没法调试新版本,只能换芯片。

3.5 安全启动链的完整流程

把上面几个环节串起来,安全启动链的完整流程是这样的:

  1. 芯片上电,BootROM 执行。
  2. BootROM 从固定位置读取第一阶段 Bootloader。
  3. BootROM 用内置公钥验证第一阶段 Bootloader 的签名。
  4. 验证通过,加载并执行第一阶段 Bootloader。
  5. 第一阶段 Bootloader 初始化硬件,读取第二阶段 Bootloader。
  6. 第一阶段 Bootloader 验证第二阶段 Bootloader 的签名。
  7. 验证通过,加载并执行第二阶段 Bootloader。
  8. 第二阶段 Bootloader 读取操作系统内核,验证签名。
  9. 验证通过,加载内核,启动系统。
  10. 每一步都检查版本号,确保没有降级。

你看,每一级都在验证下一级。这就是「链式信任」。只要根(BootROM)是安全的,整条链就是安全的。

3.6 避坑指南

最后,分享几个我踩过的坑,希望能帮你少走弯路:

  • 签名验证不能只验头部:我见过有人只验证镜像头部的哈希,结果攻击者修改了镜像主体,头部没动。验证通过了,但跑的是恶意代码。记住,要验证整个镜像。
  • 公钥不能硬编码在代码中:有人把公钥写在 C 代码里,结果被反编译出来。公钥应该放在 OTP 或 eFuse 中,或者至少经过加密存储。
  • 回滚保护要考虑恢复机制:万一版本号写错了,设备就变砖了。我建议留一个物理恢复接口(比如通过特定 GPIO 进入恢复模式),但恢复模式本身也要有安全保护。
  • 性能与安全的平衡:签名验证需要时间。如果每次启动都验证所有镜像,启动时间会很长。我一般只在关键环节做签名验证,非关键环节用哈希校验即可。

好了,安全启动链的内容就聊到这。下一章咱们聊聊「磁盘加密与文件系统安全」,到时候会讲到如何保护用户数据。敬请期待。