2. 启动校验基础:可信启动链概念、BootROM与Bootloader的分工、硬件信任根(RoT)的建立

这一节,我们聊聊嵌入式安全里最基础、也最容易被忽视的部分——启动校验。说白了,就是回答一个问题:你的设备上电后,凭什么相信第一行代码是安全的?

我见过不少产品,功能做得花里胡哨,但启动阶段完全裸奔。攻击者只要改个Flash里的镜像,设备就乖乖执行恶意代码了。嗯,这其实等于把家门钥匙挂在门外。

2.1 可信启动链:一条链,环环相扣

可信启动链(Trusted Boot Chain)不是什么高深理论。它就是一个逐级校验的过程:

  • 每一级在加载下一级之前,先验证它的签名或哈希
  • 验证通过才移交控制权
  • 验证失败就停止启动,或者进入恢复模式

你想想看,这就像接力赛。每一棒交接时都要确认对方身份。只要有一棒出了问题,整个链条就断了。

核心原则:信任不能传递,只能验证。上一级信任下一级,不是因为“应该信任”,而是因为“已经验证过”。

我在项目中遇到过一件事。某款IoT设备,BootROM校验了Bootloader,但Bootloader没校验内核。结果攻击者直接替换了内核分区,设备照样启动。你看,链条断了一环,整个安全体系就形同虚设。

2.2 BootROM与Bootloader:谁该干什么?

很多人分不清BootROM和Bootloader的职责。我习惯用一个比喻:

  • BootROM 是芯片出厂自带的“保安”,焊死在ROM里,改不了
  • Bootloader 是“项目经理”,存在Flash里,可以升级

它们的核心分工如下:

角色 存储介质 可修改性 主要职责 安全要求
BootROM 片上ROM(只读) 不可修改 初始化硬件、加载Bootloader、验证Bootloader签名 必须绝对可信,是信任根
Bootloader Flash(可读写) 可升级 初始化外设、加载OS内核、验证内核签名 必须被BootROM验证,自身也要防篡改

这里有个容易踩的坑:BootROM的代码量通常很小,几KB到几十KB。为什么?因为ROM面积贵啊。所以BootROM只做最核心的事:初始化时钟、内存、基本外设,然后验证Bootloader。

我曾经犯过的错:在设计某款车规芯片时,我把USB驱动也塞进了BootROM。结果发现ROM面积超标,而且USB协议有漏洞还得改ROM——根本改不了。后来只能做ECO,代价惨重。所以,BootROM里放的东西越少越好,只放“不变且必须”的逻辑。

2.3 硬件信任根(RoT):一切信任的起点

信任根(Root of Trust, RoT)是整个安全体系的基石。它必须满足三个条件:

  1. 不可篡改——物理上改不了
  2. 不可绕过——启动流程必须经过它
  3. 行为可预测——每次启动都做同样的事

最常见的硬件信任根就是BootROM。它固化在芯片内部,攻击者没法改。但光有BootROM还不够,你还需要一个不可变的安全存储来放公钥或哈希值。

我个人习惯把信任根分为三层:

  • 硬件层: BootROM + OTP/eFuse(存储根公钥)
  • 固件层: Bootloader(被BootROM验证)
  • 应用层: OS内核 + 应用(被Bootloader验证)

每一层都依赖上一层的验证结果。这就是“链式信任”的本质。

一个小技巧:OTP(一次性可编程存储器)里存的不一定是完整公钥。你可以存公钥的哈希,然后把公钥放在Bootloader里。BootROM先验证公钥哈希是否匹配OTP,再用公钥验证Bootloader签名。这样既节省了OTP空间,又保证了灵活性。

2.4 完整的可信启动流程

下面这张图展示了典型的可信启动链:

可信启动链流程图 可信启动链流程图 上电复位 BootROM执行 验证Bootloader签名 验证失败 → 停止启动 验证成功 → 加载Bootloader Bootloader执行 验证内核签名 信任根:BootROM + OTP

流程其实不复杂。核心就三步:

  1. BootROM 从OTP读取根公钥,验证Bootloader的签名
  2. Bootloader 验证OS内核的签名
  3. OS内核 验证关键应用或驱动的签名(可选)

每一步验证失败,系统都应该立即停止,而不是尝试“跳过”或“忽略”。

注意:有些芯片支持“安全启动”和“非安全启动”两种模式。如果你在生产环境中开了非安全启动,那前面的所有安全设计都白费了。我见过某厂商的固件,默认配置就是非安全启动——因为他们觉得“调试方便”。结果产品被爆出启动注入漏洞,紧急召回。所以,量产前一定要检查启动模式配置。

2.5 硬件信任根的建立方式

建立硬件信任根,常见的有三种方式:

方式 存储介质 优点 缺点 适用场景
OTP eFuse 芯片内部熔丝 物理不可逆,安全性高 一次性烧写,错了就废 高安全要求(车规、金融)
Mask ROM 掩膜ROM 成本低,不可修改 无法升级,灵活性差 大批量、功能固定的芯片
安全元件(SE) 独立安全芯片 隔离性好,可扩展 成本高,通信有延迟 高端手机、支付终端

我个人最常用的是OTP eFuse + BootROM的组合。原因很简单:

  • OTP可以烧写一次,量产时注入公钥哈希
  • BootROM固化在芯片里,永远改不了
  • 两者配合,攻击者既改不了ROM,也改不了OTP

这里有个细节:OTP的位一旦烧成1,就回不去了。所以量产时一定要确认公钥哈希正确。我见过有人烧错了OTP,整批芯片报废,损失几百万。嗯,这种错误犯一次就够了。

2.6 避坑指南:启动校验的常见误区

做启动校验设计,有几个坑我踩过,也看别人踩过:

  • 误区一:Bootloader不校验内核——觉得“内核是自己编译的,没问题”。攻击者改个Flash分区,你就中招了。
  • 误区二:公钥存在Flash里——Flash可读写,攻击者可以替换公钥,然后用自己的私钥签名恶意镜像。公钥必须存在OTP或ROM里。
  • 误区三:校验失败后进入“恢复模式”——恢复模式本身可能成为攻击入口。我建议校验失败就直接锁死,除非有物理防拆机制。
  • 误区四:忽略回滚攻击——攻击者不篡改镜像,而是把旧版本(有漏洞的)刷回去。所以启动校验还要检查版本号,防止回滚。

我的习惯:在Bootloader里加一个“版本计数器”,每次升级递增。BootROM验证Bootloader时,同时检查版本号是否大于等于上次记录的版本。这样就能防回滚。当然,这个计数器也要存在OTP或安全存储里。

2.7 小结

可信启动链不是什么玄学。它就是一条规则:每一级都验证下一级,验证失败就停止。硬件信任根是这条链的起点,必须物理不可改、逻辑不可绕。

做设计时,记住三点:

  1. BootROM只做最核心的事,越小越好
  2. 公钥或哈希必须存在OTP/ROM里,不能放Flash
  3. 校验失败就锁死,别给攻击者留后门