1. 安全启动基础:信任根与信任链
各位同学,咱们今天聊安全启动。说实话,这个主题我讲了快十年,每次备课还是会翻出当年的项目笔记来对照。安全启动这东西,说白了就是给嵌入式设备装一道「从娘胎里就带出来的防盗门」——不是装完系统再补,而是从芯片上电的第一条指令就开始防。
我见过太多产品,软件层做得固若金汤,结果攻击者直接从启动阶段就把整个系统替换了。嗯,咱们今天就把这个根基打牢。
1.1 什么是安全启动
安全启动(Secure Boot)不是某个具体的算法,也不是一个单独的函数。它是一套机制,确保设备只运行经过授权的、未被篡改的代码。
你想想看,设备从按下电源键到操作系统跑起来,中间要经历多少阶段?ROM代码、Bootloader、内核、文件系统……每个阶段都是攻击者的机会。安全启动要做的,就是让每个阶段都验证下一阶段的合法性。
核心定义:安全启动是一种从硬件信任根出发,逐级验证并建立信任链的启动过程。任何未经签名的代码、被篡改的镜像,都会被拒绝执行。
我在一个车规级项目中遇到过这样的情况:客户说他们的安全启动已经做完了,结果我们一测,发现Bootloader根本没检查内核签名——因为开发人员觉得「反正Flash是焊死的,没人能改」。后来我们花了三周才把整个信任链补全。所以,别想当然。
1.2 信任根(RoT)概念
信任根,Root of Trust,简称RoT。它是整个安全体系的起点,也是唯一一个「不需要被验证」的组件。
为什么不需要验证?因为它没法被验证——它是硬件里固化的、不可修改的代码或逻辑。说白了,信任根就是「我信它,因为它改不了」。
| 信任根类型 | 载体 | 特点 | 我见过的坑 |
|---|---|---|---|
| 硬件信任根 | ROM、eFuse、OTP | 不可修改,出厂即固定 | 有客户把RoT放在Flash里,一升级就没了 |
| 软件信任根 | BootROM中的固化代码 | 可读不可写,芯片掩膜时写入 | 某芯片厂掩膜后发现bug,只能重新流片 |
| 密码学信任根 | 公钥哈希、根证书 | 存储在OTP或eFuse中 | 公钥长度选错,签名验证过不了 |
我个人习惯把信任根比作「保险箱的锁芯」——锁芯本身是焊死在箱体上的,你没法换它。你要信任它,因为它就是整个安全体系的最底层。
经验之谈:选择信任根载体时,优先考虑OTP(一次性可编程存储器)或eFuse。别图便宜用Flash,Flash可以被擦写,那就不是「根」了。
1.3 信任链模型
有了信任根,接下来就是怎么把信任传递下去。这就是信任链(Chain of Trust)。
信任链的工作方式很简单:每一级代码在运行之前,先验证下一级代码的签名或哈希,验证通过才把控制权交出去。
我画了一张图,帮你理解这个链条是怎么串起来的:
这张图里,信任从最底层的BootROM开始,一级一级往上传递。每一级都只信任上一级,上一级只信任更上一级……直到信任根。
我曾经帮一个客户排查启动失败问题,发现Bootloader明明验证通过了,但内核就是起不来。查了两天,最后发现是Bootloader在验证内核时,读的是Flash里的备份分区——那个分区从来没被更新过,存的是旧版本的内核。所以,验证代码和被验证代码必须指向同一个地方,这个细节很容易被忽略。
1.4 信任链的三种常见模型
实际项目中,信任链的实现方式不止一种。我整理了三类最常见的:
- 静态信任链:启动过程中逐级验证,启动完成后信任链结束。适合功能简单的IoT设备。
- 动态信任链:系统运行期间持续验证,比如内核模块加载时也要验签。适合需要热插拔模块的设备。
- 混合信任链:启动阶段用静态链,运行阶段用动态链。我大部分项目都选这种。
注意:静态信任链有一个致命弱点——一旦系统启动完成,后续加载的代码就不再受信任链保护。如果你的设备支持内核模块动态加载,一定要用动态信任链或混合信任链。
1.5 安全启动的典型流程
咱们用一个实际案例来串一下整个流程。假设你有一块基于ARM Cortex-M的MCU,安全启动大致是这样走的:
1. 芯片上电,CPU从BootROM开始执行
2. BootROM读取eFuse中的根公钥哈希
3. BootROM从Flash读取Bootloader镜像及其签名
4. 用根公钥验证Bootloader签名
- 验证通过 → 跳转到Bootloader
- 验证失败 → 进入死循环或恢复模式
5. Bootloader读取内核镜像及其签名
6. 用Bootloader内置的公钥验证内核签名
- 验证通过 → 启动内核
- 验证失败 → 打印错误,等待恢复
7. 内核启动后,使用dm-verity验证文件系统
8. 系统正常运行
这个流程看起来简单,但每个步骤都有坑。我记得有个项目,BootROM验证Bootloader时,签名算法用的是ECDSA P-256,但eFuse里存的公钥哈希是SHA-256——算法和哈希长度不匹配,验证永远过不了。后来我们加了一条硬性规定:签名算法和哈希算法必须在设计文档里明确对应关系。
1.6 安全启动的局限性
说了这么多好处,我也得泼点冷水。安全启动不是万能的:
- 它防不了物理攻击(比如探针直接读Flash)
- 它防不了侧信道攻击(比如通过功耗分析猜密钥)
- 它防不了已经签过名的恶意代码(比如内部人员签了恶意软件)
- 它防不了启动完成后的运行时漏洞(比如缓冲区溢出)
所以,安全启动只是整个安全体系的第一道防线。后面咱们讲密钥轮换的时候,你会看到怎么和这道防线配合,形成纵深防御。
我的建议:做安全启动设计时,先画信任链图,标清楚每一级的验证方式和失败处理。图画清楚了,代码写起来才不会乱。