签名与校验机制:代码签名证书、哈希校验、回滚保护、防降级攻击
这一章,我们聊聊物联网设备更新中最容易被忽视,但也最容易翻车的环节——签名与校验。说白了,就是怎么确保你下载到设备里的固件,真的是你想要的固件,而不是被人动过手脚的恶意版本。
我在做智能门锁项目时,就遇到过这么一件事。设备OTA升级后,用户反馈门锁偶尔会自己打开。查到最后,发现是固件签名校验没做完整,攻击者通过中间人攻击替换了固件包。嗯,从那以后,我对签名校验的敬畏心就上来了。
4.1 代码签名证书:你是谁,你说了不算
代码签名证书,相当于固件的“身份证”。它解决的是信任问题——这个固件到底是谁发布的?
我个人习惯用X.509证书体系来做签名。流程大致是这样:
- 开发者在CA机构申请代码签名证书
- 用私钥对固件哈希值进行签名
- 设备端预置CA根证书,验证签名证书的合法性
- 再用证书中的公钥验证固件签名
关键点:私钥必须物理隔离存储。我见过有团队把私钥放在Git仓库里,结果被离职员工恶意签名了测试固件,差点造成批量设备变砖。
证书本身也有生命周期。一般代码签名证书有效期1-3年,设备端需要处理证书过期、吊销的情况。我建议在固件中预置多个CA根证书,并支持OCSP(在线证书状态协议)或CRL(证书吊销列表)的离线缓存。
避坑指南:我曾经在项目中只用了自签名证书,结果设备出货后,客户要求更换签名算法。因为没有预置CA根证书,只能挨个设备返厂升级。所以,哪怕初期用自签名,也要预留证书替换的接口。
4.2 哈希校验:内容有没有被改过
哈希校验解决的是完整性问题。签名保证“谁签的”,哈希保证“签的内容没变”。
常用的哈希算法有SHA-256、SHA-384。SHA-1已经不安全了,别用。我见过一些老项目还在用MD5,说实话,那跟没校验差不多。
校验流程一般是这样:
- 设备下载固件后,先计算整个固件的哈希值
- 用公钥解密签名数据,得到原始哈希值
- 两个哈希值对比,一致则通过
// 伪代码示例:固件哈希校验
uint8_t calculated_hash[32];
sha256_calculate(firmware_data, firmware_size, calculated_hash);
uint8_t decrypted_hash[32];
rsa_decrypt(signature, signature_len, decrypted_hash);
if (memcmp(calculated_hash, decrypted_hash, 32) == 0) {
// 校验通过
proceed_with_update();
} else {
// 校验失败,拒绝更新
abort_update();
}
注意:哈希校验必须在设备端完成,不能在云端校验完再告诉设备“没问题”。攻击者完全可以伪造这个“没问题”的消息。我在一个智能摄像头项目里就发现过这种漏洞——云端校验通过后下发一个flag,设备直接跳过本地校验。
4.3 回滚保护:为什么不能降级
回滚保护,说白了就是防止设备从安全的版本V2.0,降级到有漏洞的V1.0。攻击者经常利用降级攻击,让设备回到已知漏洞的旧版本,然后利用这些漏洞入侵。
实现方式主要有两种:
| 方案 | 原理 | 优缺点 |
|---|---|---|
| 版本号比较 | 设备存储当前版本号,新固件版本号必须大于当前版本号 | 简单,但版本号可被篡改 |
| 安全计数器 | 设备内部有一个单调递增的计数器,每次升级+1,新固件必须携带更大的计数器值 | 硬件级保护,难以篡改 |
| eFuse/OTP | 一次性可编程存储器,写入后不可更改,记录最高版本 | 最安全,但灵活性差 |
我个人比较推荐安全计数器+eFuse的组合。计数器放在安全芯片里,每次升级前比对,升级成功后写入eFuse。这样即使攻击者拿到了root权限,也无法伪造计数器值。
避坑指南:我曾经在一个项目中,版本号比较逻辑写在了bootloader里,但bootloader本身可以降级。攻击者先降级bootloader,再降级固件,绕过了回滚保护。所以,回滚保护必须形成闭环——bootloader和固件互相保护。
4.4 防降级攻击:攻击者的惯用伎俩
防降级攻击,其实是回滚保护的升级版。攻击者不直接降级固件,而是通过其他手段绕过校验机制。
常见的攻击手法有:
- 版本号欺骗:修改固件头部的版本号,让设备误以为旧固件是新版本
- 中间人攻击:在OTA传输过程中替换固件包,同时伪造签名
- 存储篡改:直接修改Flash中存储的版本号或计数器值
- 时间回滚:利用NTP协议漏洞,让设备时间倒退,绕过基于时间的校验
怎么防?我总结了几条硬规则:
- 版本号必须包含在签名数据中,不能单独存储
- 安全计数器使用硬件唯一密钥加密存储
- OTA通道必须使用TLS双向认证
- 设备时间不能作为安全决策的唯一依据
核心原则:所有安全相关的元数据(版本号、哈希值、签名)必须捆绑在一起签名。拆开存储,就等于给攻击者留了后门。
4.5 整体架构:把这些串起来
说了这么多,我们来看看这些机制怎么协同工作。下面这张图展示了完整的签名校验与防降级流程:
你看,整个流程是环环相扣的。从固件打包开始,哈希、签名、版本号捆绑在一起。设备端接收后,先比版本号,再验签,再算哈希,最后写入安全计数器。任何一步失败,直接拒绝升级。
最后提醒一句:签名校验和回滚保护不是“加上就完事”的功能。你需要定期审计这些机制是否真的在生效。我见过太多项目,代码里写了校验逻辑,但实际测试时发现校验函数根本没被调用——因为某个工程师觉得“先跳过,后面再加”,然后就忘了。
嗯,这一章的内容就到这里。签名与校验,说白了就是“你是谁”和“你是什么”的问题。把这两个问题回答清楚了,固件更新的安全性就有了基本保障。