因为一个OTA升级没加密,我被客户追着骂了半个月
去年做的一个网关项目出货大概三百多台分布在几个不同的工厂。功能跑得挺好数据也准客户一开始还挺满意。结果有一天半夜对方技术负责人直接甩过来一张截图——设备屏幕上弹出了一行他们完全不认识的字符串然后整台设备就卡死了。排查了三天才搞清楚问题在哪OTA升级固件是明文传输的没有签名校验有人在中间链路注入了一个伪造的固件包。设备傻乎乎地就刷进去了然后就变砖了。这事的教训值六位数。从那之后我才认真琢磨嵌入式安全到底该怎么做。先说一个很多嵌入式工程师容易忽略的事攻击面比你想象的宽得多。你可能会觉得我就一个单片机跑着谁会来黑我但现实是IoT设备一旦联网暴露的攻击面包括OTA通道、物理串口/JTAG、网络端口、Web管理界面、甚至传感器的通信总线。任何一个点没封住都是突破口。我之前那个网关串口调试功能release版本没有禁用。boot阶段有3秒的等待检测如果有人在这3秒内往UART塞0x1F固件恢复指令设备就会进入firmware recovery模式从SD卡刷固件。这个洞是客户自己发现的他们团队的一个安全研究员拿着逻辑分析仪在那捅咕了两小时就找到了。怎么说呢那一刻脸上挺挂不住的。所以嵌入式安全到底要防什么我觉得核心就三个东西1. 防固件被提取/逆向这其实是很多二次开发需求的前提。之前有客户问我们能不能把固件dump出来给他们自己改——当然不能。但如果你没做保护他们是真能自己读出来的。保护手段读保护RDP等级设到Level 2一次性熔断。STM32叫RDP Level 2其他MCU叫法不同但原理一样——设置之后debug interface彻底废掉再也读不了flash。如果用外部flash存代码或者关键数据里面的内容要加密存放。我之前用过一个方案是AES-128-CBC密钥烧在MCU内部OTP区域外部flash的密文数据只有CPU能解密。唯一ID绑定。把MCU的unique ID参与解密密钥的派生这样即使固件被读出来了换一颗芯片也跑不了。2. 防固件被篡改/注入这就是我踩的那个坑。// 一个简陋到不能再简陋的签名校验 // 以前我是这么写的——完全没校验 if (download_ota_package( pkg )) { spi_flash_erase(APP_PARTITION); spi_flash_write(APP_PARTITION, pkg.data, pkg.size); system_reboot(); } // 后来改成这样 if (download_ota_package( pkg )) { // 先验证签名 if (verify_ecdsa_signature( pkg.data, pkg.size - SIGNATURE_LEN, pkg.data pkg.size - SIGNATURE_LEN, public_key)) { spi_flash_erase(APP_PARTITION); spi_flash_write(APP_PARTITION, pkg.data, pkg.size - SIGNATURE_LEN); system_reboot(); } else { log_error(OTA signature verification failed!); report_ota_failure(ERR_SIGNATURE); } }看起来就多了几行代码但区别是一个会被黑一个不会。公钥存在内部flash的只读区域生产的时候烧进去之后熔断写保护。签名算法我们用的是ECDSA with P-256密钥对用openssl生成。这套方案后来跑了两年多没出过问题。唯一要注意的是签名验证本身不能太久——我之前用mbedTLS的软件实现在180MHz的Cortex-M4上验一次签大概要40msOTA升级时勉强能接受。如果换了更慢的MCU可能需要硬件加速器或者用更轻量的Ed25519。3. 防通信被窃听/重放TLS是基础操作但在资源受限的MCU上跑full TLS握手确实有点吃力。我们的做法是支持TLS 1.3相比1.2少了一次RTT握手快很多用PSK模式替代证书模式节省证书解析的开销如果MCU实在带不动TLS至少用DTLS Pre-shared Key做应用层加密每个消息包里带一个单调递增的sequence number防止重放攻击我之前在一个Cortex-M0的芯片上做过实验纯软件算TLS握手开不起来RAM只有16KB最后方案是MCU负责加密载荷TLS握手交给外挂的Wi-Fi模块ESP32去完成两边通过SPI传数据。说到工具有几个趁手的可以提一下MCUboot开源的bootloader支持签名验证和固件回滚用起来挺顺手。配合Zephyr或自己的RTOS都行Trusted Firmware-M如果芯片支持ARM TrustZone的话可以上隔离安全和非安全世界WolfSSL比mbedTLS更轻量的TLS库对RAM的压榨到了令人发指的程度——最小配置下ROM不到50KBSTM32TrustST自己的一套安全框架集成了secure boot、secure firmware update、secure storage说实话嵌入式安全是个挺大的话题我这边写到的也只是自己踩过坑的部分。真要系统做的话还有安全启动链chain of trust、安全日志审计、侧信道防护、甚至物理层面的防探针攻击……这些我也还在学。不过如果你问我最想说什么别觉得自己做的东西不值一黑。三百块的网关和三个亿的系统防线上的漏洞往往是一样的。