4、安全启动链(Secure Boot):从BootROM到Bootloader再到OS的信任链建立过程
安全启动链,说白了就是给设备装上一道「基因锁」。从芯片上电的第一条指令开始,每一级代码都要验证下一级的签名,环环相扣。只要有一环断了,系统就不启动。
我刚开始做嵌入式安全时,觉得这玩意儿就是个形式。直到有一次,客户的设备在野外被物理接触,攻击者通过串口注入了恶意固件……嗯,从那以后,我再也不敢轻视 Secure Boot 了。
4.1 信任链的起点:BootROM
芯片一上电,CPU 会从固定的物理地址取指。这个地址里放的是 BootROM——它是只读的,出厂后谁也没法改。BootROM 是整个信任链的「根」,也叫 Root of Trust。
BootROM 的任务很简单:
- 初始化最基本的硬件(时钟、SRAM、看门狗)
- 从固定的存储位置(比如 eMMC 的 Boot Partition 或 SPI Flash 的起始块)读取 Bootloader
- 验证 Bootloader 的数字签名
验证通过,才把控制权交给 Bootloader。验证失败,直接死循环或进入恢复模式。
关键点:BootROM 里必须硬编码一个公钥哈希(或者公钥本身),用来验签。这个公钥哈希就是信任锚点。如果攻击者能改 BootROM,那整个安全体系就崩塌了——好在 BootROM 是 Mask ROM,物理上不可写。
4.2 第二级:Bootloader 的自我验证
Bootloader 拿到控制权后,并不是直接去加载 OS。它要先验自己的「下一级」——也就是 OS 内核的签名。但这里有个细节:Bootloader 本身也可能有多个阶段。
以我常用的 U-Boot 为例,它分 SPL(Secondary Program Loader)和 U-Boot proper 两个阶段:
| 阶段 | 存储位置 | 验证者 | 被验证者 |
|---|---|---|---|
| BootROM | 芯片内部 ROM | 硬件固化 | SPL |
| SPL | eMMC Boot Partition | BootROM | U-Boot proper |
| U-Boot proper | eMMC 用户分区 | SPL | OS Kernel + DTB |
| OS Kernel | eMMC 用户分区 | U-Boot proper | 内核模块 / 文件系统 |
每一级只信任上一级。BootROM 验 SPL,SPL 验 U-Boot proper,U-Boot proper 验内核。这就是「信任链」的物理含义。
个人经验:我在项目中遇到过一个问题——SPL 验 U-Boot proper 时,验签算法用的是 RSA-2048,但 BootROM 里只支持 SHA-256 哈希。结果 SPL 里还得自己实现 SHA-512,白白浪费了 4KB 的 SRAM 空间。所以选 SoC 时,一定要确认 BootROM 支持的哈希算法和签名算法是否匹配你的需求。
4.3 第三级:OS 内核的验证
U-Boot proper 加载内核时,会做两件事:
- 从存储设备读取内核镜像(通常是 zImage 或 Image.gz)
- 用内置的公钥验证内核镜像的签名
验证通过后,U-Boot 会清理自身的内存占用,跳转到内核入口点。这时候,内核开始运行,但信任链还没结束——内核还要验证文件系统的完整性。
我习惯的做法是:内核里内置一个 dm-verity 或 fs-verity 的哈希树。内核启动后,挂载根文件系统时,逐块校验哈希。这样即使攻击者替换了文件系统里的某个二进制文件,内核也能立刻发现。
注意:千万不要把私钥放在设备上。私钥应该放在离线的签名服务器上,每次构建固件时签名。设备上只放公钥。我曾经见过一个团队,为了方便调试,把私钥写进了 U-Boot 的环境变量里……嗯,后果可想而知。
4.4 防止未授权固件运行的核心机制
Secure Boot 防的不是「固件被篡改」,而是「篡改后的固件无法运行」。攻击者就算物理上替换了 Flash 里的内容,只要没有合法的签名,设备就起不来。
具体来说,有这几道防线:
- 签名验证:每一级都用非对称算法(RSA/ECDSA)验签,防止伪造
- 回滚保护:Bootloader 里维护一个版本号计数器,禁止加载旧版本固件(防止降级攻击)
- 硬件隔离:公钥哈希烧写在 eFuse 或 OTP 里,软件无法修改
- 调试接口封锁:量产时熔断 JTAG/SWD 接口,防止攻击者绕过 BootROM 直接调试
我见过最典型的攻击手法是「降级攻击」:攻击者拿到一个旧版本固件(可能有已知漏洞),刷进设备。如果 Bootloader 不检查版本号,旧固件就能跑起来,然后利用漏洞提权。所以回滚保护不是锦上添花,是必选项。
4.5 信任链的完整流程图
下面这张图展示了从 BootROM 到 OS 的完整信任链。每个箭头代表一次签名验证,只有全部通过,系统才能正常启动。
4.6 实际部署时的避坑指南
理论说完了,聊聊落地时容易踩的坑。我踩过,也看别人踩过。
- 公钥存储位置:别放 Flash 里,放 eFuse 或 OTP。Flash 里的内容可以被物理替换,eFuse 熔断了就改不了。
- 签名算法选择:如果 SoC 硬件加速只支持 RSA-2048,就别硬上 ECDSA。软件实现 ECDSA 验签可能慢到让启动超时。
- 版本号管理:版本号计数器要用单调递增的 OTP 区域,每次升级时硬件自动加一。别用软件维护,软件可以被篡改。
- 调试接口:量产前一定要熔断 JTAG/SWD。我见过一个产品,Secure Boot 做得很好,但 JTAG 没锁,攻击者直接挂上调试器读出了公钥哈希……虽然公钥泄露本身不致命,但配合其他漏洞就能绕过验签。
- 恢复模式:Secure Boot 失败后,设备不能变砖。要留一个恢复入口(比如检测特定 GPIO 电平),进入一个最小化的安全恢复系统,允许通过签名后的升级包恢复。
曾经踩过的坑:有一次我在 SPL 里实现了验签,但忘了检查签名算法中的填充格式。攻击者构造了一个「貌似合法」的签名,利用 RSA 的 Bleichenbacher 漏洞绕过了验签。从那以后,我每次写验签代码都会对照一遍 RFC 8017,确保填充格式完全正确。
4.7 小结
安全启动链的本质,就是「信任的逐级传递」。从 BootROM 这个不可篡改的根开始,每一级都用自己的公钥验证下一级的签名。只要根是安全的,整条链就是安全的。
但要注意,Secure Boot 不是万能的。它防的是「未授权固件运行」,防不了「已授权固件里的漏洞」。所以 Secure Boot 通常和 TEE(可信执行环境)、SE(安全元件)配合使用,构成更完整的安全体系。不过那是后面章节的内容了。