3. 升级包完整性校验:哈希校验、签名校验、防篡改机制
升级包在传输和存储过程中,说白了就是「裸奔」的。你想想看,一个几百兆的固件包,从云端下到设备,中间要经过多少跳转?CDN 节点、运营商网关、本地路由器……任何一个环节被劫持,升级包都可能被替换成恶意固件。
我早年做 IoT 网关项目时,就吃过这个亏。当时为了赶工期,只做了简单的 CRC 校验,结果设备升级后大面积变砖。后来排查发现,攻击者在中间人节点上替换了升级包,CRC 值也被同步篡改了。嗯,从那以后,我对完整性校验再也不敢马虎了。
3.1 哈希校验:最基础的完整性保证
哈希校验,就是给升级包算一个「数字指纹」。哪怕文件只改了一个 bit,哈希值都会完全不一样。常用的算法有 SHA-256、SHA-384、SHA-512。
核心原则:哈希值必须通过安全通道传输。如果哈希值和升级包走同一条通道,那校验就形同虚设。
我个人的习惯是,哈希值要么预埋在设备固件中,要么通过 HTTPS + 证书绑定的方式下发。绝对不要用 HTTP 明文传输哈希值。
哈希校验流程
- 设备下载升级包到本地缓存
- 设备从安全通道获取预期的哈希值(如 SHA-256 摘要)
- 设备对下载的升级包计算哈希值
- 比对两个哈希值:一致则继续,不一致则丢弃并上报异常
// 伪代码示例:哈希校验
uint8_t expected_hash[32]; // 从安全通道获取
uint8_t computed_hash[32]; // 本地计算
sha256_calculate(ota_package, package_size, computed_hash);
if (memcmp(expected_hash, computed_hash, 32) == 0) {
// 哈希校验通过
proceed_with_update();
} else {
// 哈希校验失败,记录异常
report_anomaly(ANOMALY_HASH_MISMATCH);
abort_update();
}
避坑指南:我曾经遇到过一个案例,设备用 MD5 做哈希校验。MD5 早已被证明存在碰撞攻击,攻击者可以构造两个不同文件但 MD5 值相同。所以,绝对不要用 MD5 或 SHA-1,至少用 SHA-256。
3.2 签名校验:防伪造的核心机制
哈希校验只能保证「文件没被改」,但保证不了「文件是谁发的」。签名校验解决的就是这个问题——用非对称加密,让设备能验证升级包是否来自合法的发布者。
签名校验的流程是这样的:
- 签名端(云端):用私钥对升级包的哈希值进行签名,生成签名文件
- 验证端(设备):用预置的公钥解密签名,得到哈希值,再与本地计算的哈希值比对
签名算法选型
| 算法 | 密钥长度 | 安全性 | 设备开销 |
|---|---|---|---|
| RSA-2048 | 2048 bit | 高 | 中等(验证快,签名慢) |
| ECDSA-P256 | 256 bit | 高 | 低(适合资源受限设备) |
| Ed25519 | 256 bit | 极高 | 极低(性能最优) |
我个人比较推荐 ECDSA 或 Ed25519。尤其是 Ed25519,签名小、验证快,在 ARM Cortex-M 系列芯片上表现非常好。RSA 虽然成熟,但密钥太长,对 IoT 设备不太友好。
// 伪代码示例:签名校验(使用 ECDSA)
uint8_t signature[64]; // 从升级包中提取
uint8_t hash[32]; // 本地计算的哈希值
uint8_t public_key[64]; // 预置在设备中的公钥
if (ecdsa_verify(public_key, hash, signature) == 0) {
// 签名校验通过
proceed_with_update();
} else {
// 签名校验失败,记录异常
report_anomaly(ANOMALY_SIGNATURE_INVALID);
abort_update();
}
注意:公钥的存储位置非常关键。如果公钥存储在可读写的 Flash 区域,攻击者可以替换公钥,然后用自己的私钥签名恶意固件。公钥必须存储在一次性写入的区域(如 OTP、eFuse),或者由 BootROM 在启动时加载。
3.3 防篡改机制:多层防护体系
哈希校验和签名校验是「事后检测」,防篡改机制则是「事中防御」。说白了,就是让攻击者即使拿到了升级包,也很难在不被发现的情况下篡改它。
常见的防篡改手段
- 加密传输:使用 TLS/DTLS 加密升级包的传输通道,防止中间人篡改
- 分块校验:将升级包分成多个小块,每块独立计算哈希值。这样即使某一块被篡改,也能精确定位
- 顺序校验:要求设备按顺序处理升级包,乱序或重复包直接丢弃
- 版本号绑定:在签名中绑定版本号,防止攻击者用旧版本的合法固件进行「回滚攻击」
我曾经踩过的坑:有个项目只做了签名校验,没做版本号绑定。结果攻击者拿到了一个旧版本的合法签名固件,用它来降级设备,然后利用旧版本的漏洞进行攻击。从那以后,我每次做 OTA 都会把版本号、设备 ID、时间戳一起签进签名里。
3.4 知识体系总览
下面这张图,把升级包完整性校验的三大块串起来了。你可以看到,哈希校验是基础,签名校验是核心,防篡改机制是补充。三者缺一不可。
3.5 实践建议
说了这么多,落地的时候要注意什么?我总结了几条:
- 不要只做哈希校验。哈希校验防不住中间人替换整个包(包括哈希值)。必须配合签名校验。
- 签名算法选 Ed25519 或 ECDSA。RSA 虽然成熟,但密钥太长,对 IoT 设备不友好。
- 公钥要锁死。存储在 OTP 或 eFuse 中,确保一次写入后不可修改。
- 签名内容要丰富。把版本号、设备 ID、时间戳都签进去,防止回滚和重放攻击。
- 校验失败要上报。不要静默处理,记录异常日志并上报云端,便于分析攻击行为。
最后提醒一句:完整性校验不是「做完就完事」的。攻击手段在进化,你的校验策略也要跟着升级。定期审视你的 OTA 安全方案,看看有没有新的攻击面暴露出来。