4、安全启动链:信任根(RoT)建立、BootROM安全、U-Boot安全加固
安全启动链,说白了就是给设备装上一道「从娘胎里就带出来的信任机制」。我做了这么多年固件安全,见过太多设备因为启动链被攻破,整个系统就像纸糊的一样。你想想看,如果连最底层的启动代码都不安全,上层做得再花哨也没用。
这一章我们重点聊三个东西:信任根怎么建立、BootROM 有哪些坑、U-Boot 怎么加固。嗯,咱们一个一个来。
4.1 信任根(RoT)的建立
信任根是整个安全启动链的基石。它必须是一个不可篡改的、硬件级别的信任锚点。我个人习惯把 RoT 理解为「第一个说真话的人」——如果第一个人就撒谎,后面全完蛋。
核心原则:信任根必须是硬件绑定的、一次性写入的、不可回滚的。
常见的 RoT 实现方式有几种:
- 一次性可编程存储器(OTP):比如 eFuse、反熔丝。芯片出厂时烧录公钥哈希或根证书,之后谁也别想改。
- 硬件安全模块(HSM):独立的安全芯片,比如 TPM、SE。专门干签名验证的脏活累活。
- CPU 内置 BootROM:掩膜 ROM,出厂就固化,物理上改不了。
我在项目中遇到过一件事:某客户用了一颗 SoC,OTP 里只烧了 128 位哈希,结果发现哈希碰撞风险太高。后来我建议他们改用 256 位,配合硬件加速引擎,才把这事摆平。
我的建议:OTP 空间很贵,别啥都往里塞。只存根公钥的哈希(256位足矣),其他证书链放 Flash,启动时逐级验证。
4.2 BootROM 安全
BootROM 是芯片上电后执行的第一段代码。它固化在掩膜 ROM 里,理论上改不了。但「改不了」不代表「攻不破」。
BootROM 常见的安全问题,我列几个典型的:
| 漏洞类型 | 风险描述 | 真实案例 |
|---|---|---|
| 调试接口未锁定 | JTAG/SWD 在 BootROM 阶段可访问 | 某路由器芯片,JTAG 直读 BootROM 代码 |
| USB 下载模式绕过 | 通过特定 USB 序列进入下载模式,跳过签名校验 | 某手机芯片,短接两个引脚直接刷机 |
| 时序攻击 | 签名验证耗时不同,泄露密钥信息 | 某 IoT 芯片,通过电源纹波分析破解 |
| 缓冲区溢出 | BootROM 解析外部输入时未做边界检查 | 某车规芯片,通过 CAN 报文触发溢出 |
我曾经帮一个客户做 BootROM 安全审计,发现他们的 BootROM 在读取外部 SPI Flash 时,没有对地址范围做校验。攻击者只要在 Flash 特定位置放一段恶意数据,就能让 BootROM 跳转到任意地址执行。嗯,这种低级错误其实挺常见的。
避坑指南:我曾经见过一个团队,BootROM 里留了个「工厂测试模式」,出厂时忘了熔断 OTP 里的禁用位。结果被攻击者利用这个模式直接 dump 了整个固件。记住:所有调试接口、测试模式,量产时必须永久关闭。
4.3 U-Boot 安全加固
U-Boot 是嵌入式 Linux 世界用得最多的 bootloader。它功能强大,但攻击面也大。我见过太多设备,U-Boot 裸奔着就上线了——没有签名校验、没有环境变量保护、甚至串口都没锁。
U-Boot 安全加固,我一般按这几个维度来:
4.3.1 签名验证机制
U-Boot 从 v2016 开始支持 verified boot。核心思路是:U-Boot 本身由 BootROM 验证,然后 U-Boot 再去验证内核和设备树。
// 配置 verified boot 的关键选项
CONFIG_FIT_SIGNATURE=y
CONFIG_RSA=y
CONFIG_RSA_SOFTWARE_EXP=y
CONFIG_OF_CONTROL=y
CONFIG_SPL_FIT_SOURCE="uboot-fit.its"
签名流程大致是这样:
- 生成 RSA 密钥对(私钥自己保管好,公钥烧进 OTP)
- 用私钥对 FIT image 签名
- U-Boot 启动时,用 OTP 里的公钥验签
- 验签通过才加载内核,否则死循环或进恢复模式
关键点:公钥必须从 OTP 读取,不能从 Flash 读。否则攻击者可以替换公钥,签名校验形同虚设。
4.3.2 环境变量保护
U-Boot 的环境变量默认存在 Flash 里,明文存储。攻击者只要改一个 bootargs,就能让内核启动时挂载恶意文件系统。
我建议的做法:
- 环境变量加密:用 AES 加密存储,密钥从 OTP 派生
- 环境变量签名:每次写入时附带 HMAC,启动时校验完整性
- 锁定关键变量:比如 bootcmd、bootargs,设为只读
// 环境变量加密配置示例
CONFIG_ENV_AES=y
CONFIG_ENV_AES_KEY="your-256-bit-key-here"
CONFIG_ENV_IS_IN_MMC=y
CONFIG_ENV_SIZE=0x20000
我的经验:别把加密密钥硬编码在源码里。我见过有人把密钥写在头文件里,结果代码托管平台泄露了。正确的做法是从 OTP 或安全芯片派生密钥。
4.3.3 串口与调试接口锁定
U-Boot 的串口控制台是攻击者的最爱。只要接上串口线,按个回车,就能进入 U-Boot 命令行,为所欲为。
加固措施:
- 密码保护:进入命令行需要密码,密码不能明文存
- 禁用危险命令:比如 sf probe、mmc write、fatload 等
- 超时自动启动:串口无输入 3 秒后自动加载内核,不给攻击者操作时间
// 禁用危险命令的配置
CONFIG_CMD_SF=n
CONFIG_CMD_MMC=n
CONFIG_CMD_FAT=n
CONFIG_CMD_EXT4=n
CONFIG_CMD_ITEST=n
// 设置密码(哈希存储)
setenv bootdelay 3
setenv stdin_password 'sha256:xxxx...'
注意:我曾经遇到一个案例,客户禁用了所有危险命令,但忘了禁用「reset」命令。攻击者通过反复复位设备,在特定时序下触发了 BootROM 的漏洞。所以,攻击面分析要全面,不能只看 U-Boot 本身。
4.4 安全启动链的完整流程
把上面这些串起来,一个完整的安全启动链应该是这样的:
这个流程里,每一级都只信任上一级的签名结果。BootROM 信任 OTP 里的公钥,U-Boot 信任 BootROM 的验证结果,内核信任 U-Boot 的验证结果。这就是所谓的「信任链」——环环相扣,缺一不可。
总结一下:安全启动链不是某个单一技术,而是一整套从硬件到软件的信任传递机制。OTP 存根、BootROM 验签、U-Boot 加固,每一环都得做到位。我在项目中见过太多「木桶效应」——明明 BootROM 很安全,结果 U-Boot 裸奔,整个安全链直接崩了。
嗯,这一章的内容就到这。记住:安全启动链是固件安全的第一道防线,也是最后一道防线。搞砸了它,后面做再多加固都是白搭。