3. 升级包完整性校验:哈希校验、签名校验、防篡改机制

升级包在传输和存储过程中,说白了就是「裸奔」的。你想想看,一个几百兆的固件包,从云端下到设备,中间要经过多少跳转?CDN 节点、运营商网关、本地路由器……任何一个环节被劫持,升级包都可能被替换成恶意固件。

我早年做 IoT 网关项目时,就吃过这个亏。当时为了赶工期,只做了简单的 CRC 校验,结果设备升级后大面积变砖。后来排查发现,攻击者在中间人节点上替换了升级包,CRC 值也被同步篡改了。嗯,从那以后,我对完整性校验再也不敢马虎了。

3.1 哈希校验:最基础的完整性保证

哈希校验,就是给升级包算一个「数字指纹」。哪怕文件只改了一个 bit,哈希值都会完全不一样。常用的算法有 SHA-256、SHA-384、SHA-512。

核心原则:哈希值必须通过安全通道传输。如果哈希值和升级包走同一条通道,那校验就形同虚设。

我个人的习惯是,哈希值要么预埋在设备固件中,要么通过 HTTPS + 证书绑定的方式下发。绝对不要用 HTTP 明文传输哈希值。

哈希校验流程

  1. 设备下载升级包到本地缓存
  2. 设备从安全通道获取预期的哈希值(如 SHA-256 摘要)
  3. 设备对下载的升级包计算哈希值
  4. 比对两个哈希值:一致则继续,不一致则丢弃并上报异常
// 伪代码示例:哈希校验
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 知识体系总览

下面这张图,把升级包完整性校验的三大块串起来了。你可以看到,哈希校验是基础,签名校验是核心,防篡改机制是补充。三者缺一不可。

升级包完整性校验知识体系 哈希校验 签名校验 防篡改机制 SHA-256 / SHA-384 / SHA-512 安全通道传输哈希值 本地计算 + 比对 RSA / ECDSA / Ed25519 私钥签名 + 公钥验证 公钥安全存储(OTP/eFuse) TLS/DTLS 加密传输 分块校验 + 顺序校验 版本号绑定防回滚 三者缺一不可:哈希校验是基础,签名校验是核心,防篡改是补充 攻击者即使突破一层,还有两层等着 多层防护 = 纵深防御

3.5 实践建议

说了这么多,落地的时候要注意什么?我总结了几条:

  • 不要只做哈希校验。哈希校验防不住中间人替换整个包(包括哈希值)。必须配合签名校验。
  • 签名算法选 Ed25519 或 ECDSA。RSA 虽然成熟,但密钥太长,对 IoT 设备不友好。
  • 公钥要锁死。存储在 OTP 或 eFuse 中,确保一次写入后不可修改。
  • 签名内容要丰富。把版本号、设备 ID、时间戳都签进去,防止回滚和重放攻击。
  • 校验失败要上报。不要静默处理,记录异常日志并上报云端,便于分析攻击行为。

最后提醒一句:完整性校验不是「做完就完事」的。攻击手段在进化,你的校验策略也要跟着升级。定期审视你的 OTA 安全方案,看看有没有新的攻击面暴露出来。