4、安全体系设计:安全启动链(Secure Boot)、TLS/DTLS传输加密、固件签名验证、防回滚机制

各位同学,咱们今天聊点硬核的——医疗设备的安全体系。说实话,这章内容我每次讲都觉得责任重大。你想想看,一个心脏起搏器或者胰岛素泵,如果被黑客远程篡改了固件,那可不是丢数据的问题,是要出人命的。我在这个领域摸爬滚打十几年,见过太多「裸奔」的设备,想想都后怕。

所以,安全体系不是锦上添花,是底线。今天咱们就拆开揉碎了讲四个核心模块:安全启动链、传输加密、签名验证、防回滚。这四个东西,说白了就是一套组合拳,少一个都不行。

4.1 安全启动链(Secure Boot)

安全启动链,我习惯叫它「信任的起点」。为什么这么说?因为设备上电后,第一段代码是谁,决定了整个系统的生死。

咱们的MCU或者MPU,内部通常有一个不可篡改的ROM Bootloader。它只做一件事:验证下一级Bootloader的签名。验证通过,才把控制权交出去。一级验一级,像链条一样,直到操作系统内核被加载。

我个人习惯把这个过程分成三步:

  1. 硬件根信任:芯片出厂时烧录一个公钥哈希(或者直接烧公钥),这个区域是OTP(一次性可编程),谁也改不了。
  2. 逐级验证:ROM Bootloader验证SPL(Secondary Program Loader),SPL验证U-Boot,U-Boot验证内核。每一级都携带数字签名。
  3. 失败处理:如果任何一级验证失败,直接死循环或者进入恢复模式。绝对不能继续执行。

核心原则:信任链必须从硬件开始,且不可逆。任何软件层面的「信任」都是耍流氓。

我在项目中遇到过一件事:某款设备为了省成本,把公钥存在了SPI Flash里。结果呢?攻击者直接拆了Flash,替换公钥和固件,设备就「叛变」了。嗯,这就是典型的信任根没扎在硬件里。

4.2 TLS/DTLS传输加密

固件升级和远程维护,数据要经过网络。网络是什么?是公共厕所,谁都能来。所以传输加密是必须的。

这里有两个选择:TLSDTLS

特性 TLS DTLS
传输层协议 TCP UDP
可靠性 可靠(自带重传) 不可靠(应用层处理)
延迟 较高(握手3次) 较低(握手1.5次)
适用场景 固件下载、配置下发 遥测数据、心跳包

我个人建议:固件升级用TLS,因为数据量大且要求完整;远程维护的实时控制用DTLS,因为延迟敏感。

你可能会问:「直接用HTTPS不行吗?」行,但HTTPS就是HTTP over TLS。如果你的设备资源受限(比如只有几百KB RAM),跑完整TLS栈可能吃力。这时候可以考虑mbedTLS或者WolfSSL,它们对嵌入式很友好。

避坑指南:我曾经见过一个团队,为了省事,把TLS证书写死在代码里。结果证书过期那天,所有设备都连不上服务器了。记住:证书要有轮换机制,最好支持远程更新。

4.3 固件签名验证

固件签名,说白了就是给固件打个「防伪标签」。设备在升级前,必须验证这个标签是真的,才允许写入。

签名流程一般是这样的:

  1. 服务器端:用私钥对固件哈希值进行签名,生成签名文件。
  2. 设备端:用公钥解密签名,得到哈希值A;再计算固件本身的哈希值B。
  3. 比较:如果A == B,说明固件是官方发布的,且没有被篡改。

这里要注意几个细节:

  • 哈希算法:别用MD5或SHA-1了,至少用SHA-256。我见过有人用CRC32做校验,那玩意儿连防碰撞都做不到。
  • 签名算法:推荐ECDSA(椭圆曲线),比RSA快,密钥更短。对于资源受限的设备,ECDSA是首选。
  • 公钥存储:公钥必须放在安全区域,比如MCU内部的eFuse或者安全元件(SE)。绝对不能放在文件系统里。
// 伪代码示例:固件验证流程
bool verify_firmware(const uint8_t* firmware, size_t len, const uint8_t* signature) {
    // 1. 计算固件哈希
    uint8_t hash[32];
    sha256(firmware, len, hash);
    
    // 2. 用公钥解密签名,得到原始哈希
    uint8_t decrypted_hash[32];
    ecdsa_verify(public_key, signature, decrypted_hash);
    
    // 3. 比较
    return memcmp(hash, decrypted_hash, 32) == 0;
}

警告:签名验证必须在设备本地完成,且不能依赖网络。我曾经见过一个设计:设备把固件哈希发到云端验证。结果攻击者伪造了云端响应,直接绕过了验证。记住:本地验证是底线。

4.4 防回滚机制

防回滚,很多人容易忽略。你想想看:攻击者发现新固件有安全补丁,打不进去了。怎么办?他可以把设备回滚到有漏洞的旧版本,然后利用已知漏洞攻击。

防回滚的核心思路就一句话:只允许升级,不允许降级

具体实现方式:

  • 版本号比较:固件头里包含版本号,设备升级前检查新版本号是否大于当前版本号。小于等于就拒绝。
  • 版本号存储:版本号必须存储在安全区域(比如OTP或者加密Flash)。如果存在普通Flash,攻击者可以改掉。
  • 熔断机制:对于极其关键的设备,可以在硬件上设计熔丝。一旦升级到新版本,旧版本的熔丝就被烧断,再也回不去了。

我个人的经验是:版本号比较是最简单的,但也是最容易被绕过的。如果你用普通Flash存版本号,攻击者可以直接把版本号改成0xFFFFFFFF,然后刷入旧固件。所以,版本号必须和签名绑定在一起。也就是说,签名验证时,不仅要验证固件内容,还要验证版本号。

最佳实践:将版本号作为签名数据的一部分。这样,攻击者无法单独修改版本号而不破坏签名。

另外,还有一个容易被忽略的点:回滚测试。我在项目中遇到过,新固件有bug,需要紧急回滚到旧版本。但防回滚机制不允许降级,导致设备变砖。所以,防回滚不是绝对的,要留一个「安全通道」——比如通过物理按键进入恢复模式,或者通过专用的调试接口强制刷写。但这个通道必须受控,不能成为攻击入口。

小结

好了,这四个模块咱们都过了一遍。安全启动链建立信任根,TLS/DTLS保护传输过程,签名验证确保固件来源可靠,防回滚堵住降级攻击。这四个东西,缺一个,整个安全体系就是纸糊的。

最后送大家一句话:安全不是功能,是属性。别等到设备被攻破了才后悔。嗯,今天就到这里,下节课咱们聊聊OTA升级的断点续传和失败恢复策略。