公司动态
MCU增强安全实战:安全启动、加密存储与防回滚
做嵌入式这些年我见过太多把“联网功能”做完就急着量产的MCU项目。传感器数据直接明文上云固件包不签名不校验调试串口留着出厂默认密码——直到某天发现设备被人远程改了配置或者某批货被抄板抄得底裤都不剩才开始回头补安全课。这几年行业里反复强调“MCU Delivers Enhanced Security for Connected Devices”不是芯片厂商想多卖几颗带安全特性的料而是连接设备的安全问题已经从“会不会被黑”变成了“什么时候被黑、损失多大”。这篇内容就围绕MCU如何为联网设备提供增强安全把硬件安全机制、工程落地的步骤和实战中的坑一次讲透。适合刚接触物联网安全的嵌入式工程师也适合还在纠结下一代产品该怎么选型的产品经理。1. 从“联网就是裸奔”说起MCU安全为什么突然成了刚需1.1 我先讲一个真实翻车案例有个做智能净水器的客户找到我他们的设备卖了几万台MCU用的是市面上很常见的一颗ARM Cortex-M4性能足够、生态成熟价格也压得低。但问题恰恰出在“大家都在用”这件事上——他们固件里用了一个固定的AES密钥密钥就硬编码在Flash里没有做任何读保护。结果有人把固件整个dump出来用IDA翻了几分钟从一个字符串表里直接找到了密钥。后面的事你应该猜到了攻击者伪造设备鉴权请求用脚本批量领走了运营平台上的免费滤芯兑换券等活动运营发现的时候账面损失已经过百万平台信誉更是无法估量。这个案例里那颗MCU本身并不是脆弱点真正脆弱的是整个安全设计思路。我后来帮他们做整改发现的问题远不止密钥硬编码这一处固件升级没有任何签名校验调试接口处于完全开放状态通信协议里没有随机数防重放。可以说攻击者根本不需要什么高端攻击手法前前后后就是“读取Flash、提取密钥、模拟设备”这三板斧纯粹是设计上把大门敞开给了别人。1.2 为什么以前不重视现在必须重视早些年MCU设备大多是本地简单控制最多通过GPRS或WiFi做透传攻击者缺乏足够的利益驱动去针对一个温控器或电表。但现在情况完全变了尤其是智能家居、工业物联网、车联网大规模落地之后攻击动机变得非常多样设备里有用户隐私数据比如指纹、人脸、位置、支付凭证。设备本身可以被利用发动大流量攻击大量装有默认弱口令的IP摄像头组成的僵尸网络一次DDoS就能把一家中型网站打瘫痪。抄板和仿冒带来的商业损失在智能锁、无人机、医疗设备等品类上尤为严重仿冒品一旦出安全事故责任还会追溯回原厂。所以我现在跟团队聊MCU安全很少谈“技术先进性”更多谈“商业风险”。安全不是为了让方案炫酷而是为了降低设备从出厂到退役整个生命周期里可能发生的业务损失。这是我做安全方案选型时的第一出发点。1.3 到底哪些设备真正需要MCU级安全并不是所有设备都需要同样等级的安全防护一上来就堆满TrustZone、安全元件、TPM成本撑不住开发周期也拖不起。我习惯把设备分成三档来评估设备档位典型设备安全需求等级建议方案低价值/一次性设备电子货架标签、一次性医疗贴片基础防护防普通用户读取即可开启读保护固件简单加密中端智能设备智能门锁、摄像头、传感器节点必须防固件提取、防协议重放、防仿冒安全启动、硬件加密引擎、安全存储、DTLS高价值/工业/车规设备汽车域控制器、医疗仪器、工业网关满足功能安全与信息安全双重标准TrustZone、HSM/安全元件、完整证书体系、抗故障注入这个分级很重要我见过很多项目的问题不是“没做安全”而是“安全做得过重”一个LED灯泡也要上安全启动和可信执行环境结果成本翻倍、功耗超标最后方案被产品经理毙掉。反过来一个智能门锁却只用软件AES加密通信那才是真正的灾难。做安全的第一步是先定义清楚自己的威胁模型而不是把厂商的Datasheet里所有安全特性全部打勾。2. 增强安全的底层逻辑MCU安全机制全景拆解2.1 安全启动从第一行代码就开始信任安全启动Secure Boot是MCU安全体系的基石。它的核心逻辑不复杂设备上电后最先执行的代码必须来自一个不可篡改的信任根然后由信任根逐级校验后续代码的完整性和签名。换句话说就是从BootROM到Bootloader再到App每一级的合法身份都由上一级验证形成一个信任链。以ARM Cortex-M系列为例芯片出厂时BootROM是掩膜在硅片上的不可修改这就是天然的信任根。BootROM启动后会读取存放在Flash里的Bootloader并使用芯片固化的非对称公钥验证Bootloader的签名通过后再跳转。之后Bootloader再用自己的公钥去验证App镜像。这样做的好处是即使攻击者拿到了Flash中的固件只要他没有对应的私钥就没法制造出能通过校验的App镜像设备就不会执行他的恶意代码。在我的实际操作中安全启动最容易被忽略的一个点是公钥的存储。公钥必须放在一次性可编程OTP区域或者受保护的安全存储区域里并且要确保设备生命周期内不能被覆盖。有些方案为了方便调试把公钥放在可读写的Flash分区里这等于把信任链的锚点拔掉了攻击者只要替换公钥就能用自己的私钥签任意固件。我之前检查过的一个客户产品就是这个漏洞攻击者没有破解任何加密只是合法地把自定义固件刷进了设备——因为公钥可写安全启动形同虚设。2.2 加密引擎与真随机数算法不能是软肋很多MCU开发者在早期做产品原型时习惯用软件AES库来加密数据因为改起来方便。但真正进入量产时我强烈建议切换到硬件加密引擎原因有几个性能差异巨大。软件AES在Cortex-M0上做一次128位分组的加解密可能要几千甚至上万个周期而硬件加密引擎通常几十个周期搞定。对需要持续加密通信的设备来说这是数量和功耗的差距。软件实现容易出时序侧信道漏洞。如果AES的S盒查找或密钥加载没有做成常量时间攻击者通过功耗分析或电磁辐射分析理论上可以反推出密钥。而硬件加密引擎在设计时通常已经考虑了这些物理攻击的防护。硬件密钥隔离。很多MCU的硬件加密引擎可以直接引用位于安全存储区域的密钥句柄软件根本拿不到明文密钥这比软件里定义一个全局数组存储密钥要安全得多。真随机数发生器TRNG也是MCU安全里常被忽视的一环。生成会话密钥、生成信任状Nonce、做密钥交换时都需要真随机数。如果用伪随机数发生器PRNG且种子可预测那么一切加密协议都会失去意义。我的经验是量产前一定要用NIST SP 800-22或者相近的随机性测试套件对芯片的TRNG输出做一遍测试别只看Datasheet上写着“支持TRNG”就完事了。2.3 安全存储与密钥管理密钥丢了等于没锁门MCU安全存储要做到“即使攻击者能读Flash也读不到密钥”。这听起来矛盾但现代MCU通过以下几种方式实现专用的安全Flash区域外部CPU通过正常总线无法直接访问只有安全子系统或加密引擎可以读取。总线加密即使通过调试器或DMA方式去读读到的也是密文。密钥与硬件绑定比如利用芯片唯一IDUnique ID参与密钥派生密钥换到另一颗芯片上就无法使用。密钥管理不仅要解决“怎么存”还要解决“怎么分”。我建议在MCU系统里采用三级密钥体系主密钥设备出厂时烧录用于保护其他密钥、会话密钥每次通信动态协商用于加密实际数据、数据密钥用于加密大块业务数据比如存储在外部Flash中的日志。主密钥存在于安全存储区域且永不出现在软件中会话密钥和数据密钥由主密钥派生加密后存储。这样做的好处是即使某次会话密钥被暴力破解也不会影响到其他设备和其他会话的数据安全。这里还要特别提醒一点不要把任何密钥放在外部串行Flash或者EEPROM里除非你用了带硬件密封的安全元件来封装。外部Flash芯片的引脚和内容读取太容易了拿个USB转SPI工具就能把里面的内容全部读出来放在那里等于把钥匙放在门垫底下还画了个箭头标注。2.4 隔离与TrustZone把敏感操作关进保险箱ARMv8-M架构引入的TrustZone技术是MCU安全从“外挂补丁”走向“内生安全”的重要分水岭。它把Cortex-M23、Cortex-M33和Cortex-M55这些内核的运行环境划分为安全世界Secure World和普通世界Non-secure World配合内存保护单元MPU和总线层面的隔离让安全组件和非安全组件运行在同一颗芯片上却互不干扰。在我实际项目中的推荐划分方式是把密钥管理、安全启动校验、安全固件升级、安全协议栈放到安全世界运行普通应用程序逻辑跑在普通世界两边通过专用的安全调用接口类似函数调用但经过安全门控通信。这样即使普通世界的应用被攻击者完全控制也无法直接读取安全世界的内存和密钥。需要说明的是TrustZone并不是万能的。它主要解决“软件隔离”问题对物理攻击比如电压毛刺、激光故障注入的抵御能力有限。如果你的产品面对的攻击者是具备实验室条件的专业团队那么应该考虑在TrustZone之外再加一颗独立的安全元件Secure Element或者HSM硬件安全模块把最重要的密钥和密码学运算放到独立芯片里。我在高价值工业网关项目上就是这么干的MCU负责通信和应用逻辑安全元件负责存储根密钥和执行关键签名/验签操作即使MCU完全沦陷攻击者也拿不到根密钥也无法伪造原厂的签名。3. 实操实录给一个低功耗MCU设备加上安全防护3.1 硬件选型哪些芯片自带“安全天赋”如果你是在做新产品选型我的建议是把安全特性作为“默认必选”而不是“加分项”而不是最后再找一个外挂安全芯片来弥补。目前市面上主流的安全MCU可以分为几类系列内核安全特性适用场景STM32H5 / STM32U5Cortex-M33TrustZone、硬件加密、Secure Boot、抗故障注入工业控制、智能家居中高端设备NXP LPC55xx / i.MX RTCortex-M33TrustZone、PRINCE外设保护、错误注入检测物联网网关、边缘计算设备瑞萨 RA6 / RA8Cortex-M33/M85TrustZone、安全加密引擎、篡改检测工业传感、楼宇自动化Microchip PIC32CKCortex-M33TrustZone、安全启动、PUF医疗设备、工业HMIESP32-C3 / ESP32-S3RISC-V / Xtensa安全启动、Flash加密、数字签名外设智能家居、消费类IoT选型时不要只看“有没有TrustZone”这个标签还要看几个容易被忽略的细节芯片是否支持多级读保护、是否支持安全密钥注入的产线方案、是否提供安全启动的完整参考代码、安全相关的功耗是不是在可接受范围。我用过一些芯片Datasheet上写得很好但SDK里的安全启动参考实现是半成品还需要自己补量产时间很容易被拖垮。3.2 启动链路改造从BootROM到App分区的信任链设计假设你已经在用一颗带安全启动的MCU现在要开始改造工程。这里我给你一个可落地的步骤以STM32U5和MCUboot方案为例生成密钥对。用openssl或者MCUboot的imgtool工具生成一组ECDSA P-256公私钥对私钥保存在受控的签名服务器或HSM中公钥后续烧录到芯片。# 使用imgtool生成密钥对 imgtool keygen -k signing_keys.pem -t ecdsa-p256烧录公钥到OTP区域。芯片上电后BootROM会从OTP读取公钥哈希来校验Bootloader。所以公钥必须完整烧入OTP并且把OTP锁定为不可写。编译Bootloader。MCUboot作为独立Bootloader编译时配置签名验证的算法和公钥位置。把生成的App固件用私钥签名。# 为App镜像签名 imgtool sign --key signing_keys.pem --align 8 --version 1.0.0 \ --header-size 0x400 --pad-header \ app.bin signed_app.bin规划Flash分区。至少划分为Bootloader区、主App区、App备份区、配置/密钥存储区分区起始地址必须与MCU的对齐要求一致否则签名校验的头部解析会出错。启动流程验证。上电后通过调试串口或日志确认BootROM是否成功验证并跳转BootloaderBootloader是否成功验证App签名如果App签名错误设备是否进入安全恢复模式而不是直接运行错误代码。我在实际项目里被坑过的一个细节是签名时会把App的元数据版本号、加载地址、长度一起放进签名范围的如果Bootloader里配置的加载地址和你实际编译的链接地址不一致签名就会校验失败。这类问题排查起来非常恼人因为现象只是“设备启动不了”但日志里不会直接告诉你“加载地址不匹配”。建议在量产前的启动联调阶段把Flash分区和链接地址做成一个独立的配置头文件从源头避免两端不一致。3.3 密钥烧录与生命周期管理产线环节最容易被忽视安全启动和TrustZone再完善最终都要过“产线烧录”这一关。很多项目在研发阶段跑通了安全流程一到产线就抓瞎几千片PCB等着烧录每片都要注入不同的密钥原始私钥不能暴露给代工厂。这个环节如果设计不当要么效率极低要么密钥泄露。我推荐的做法是引入HSM辅助产线烧录方案。具体流程如下在安全的生产环境中由HSM生成设备密钥对或者由HSM生成一条包含加密密钥的烧录镜像。通过编程器或产线软件将公钥、设备证书和安全启动配置一次性写入MCU写入后立即将调试接口锁死到最高等级。私钥永远不直接出现在产线软件和代工厂的计算机中。烧录数据通过加密通道从服务器下发到编程器编程器再通过安全协议与MCU完成通信。产线结束后对每一片MCU的生产记录哈希值、序列号、烧录时间做审计方便后续追溯。开发阶段和量产阶段必须使用不同的密钥体系。我见过一个团队为了省事开发板和生产板共用同一套签名私钥结果一个离职工程师的电脑里拷出了私钥文件整个产品线的安全信任全部崩塌。靠谱的做法是至少准备三套密钥环境开发环境、试产环境、量产环境每套密钥在对应的环境内使用并且在代码和SDK中通过编译宏区分。3.4 通信安全落地TLS/DTLS在MCU上的裁剪MCU资源有限跑完整版的TLS 1.3握手对内存和CPU都是不小的负担。我介绍一个在Cortex-M33低功耗设备上落地的通信安全方案。协议栈选择优先使用mbedTLS并启用其PSA Crypto API接口。mbedTLS支持模块裁剪你可以把不需要的算法全部关掉减少内存占用。算法选择在密钥交换方面优先使用ECDHE_ECDSA因为ECDSA证书比RSA证书短握手计算量也更小对称加密用AES-CCM或AES-GCM避免使用CBC模式减少填充预言攻击的风险。证书链精简把根证书预置在设备安全存储里设备只保留一张终端实体证书或直接使用预共享密钥就能显著降低握手负担。使用DTLS的PSK模式如果设备走的是CoAP DTLS可以启用预共享密钥模式而不是完整的证书验证。PSK模式握手轮次少、内存占用低非常适合资源受限的传感器节点。通信安全的一个常见误解是“只要加密了数据就安全”。我提醒团队成员加密只能保证机密性和一定程度的完整性但防不了重放攻击。因此协议里必须带上Nonce和序列号并且在设备端记录最近的序列号窗口才能防止攻击者把抓包到的合法指令重新发给设备。我在这上面吃过亏当时设备与控制平台之间的消息用了AES加密但没有序列号攻击者只需要重放一个“开锁”的加密报文就能反复开门平台侧完全无法分辨后果相当严重。4. 攻击与防御的猫鼠游戏常见安全失效场景与排查4.1 故障注入与侧信道物理接近就能破解有些团队以为MCU只要设了密码和读保护就万事大吉但当攻击者可以物理接触设备时攻击手段立刻升级。故障注入Fault Injection是常见的物理攻击手段之一攻击者通过电压毛刺、时钟毛刺或激光照射让芯片在执行安全校验时跳过一个关键分支从而绕过签名验证或者读保护。我在评估一款家用智能门锁时做过一次模拟攻击测试把电压毛刺打在MCU的供电引脚上瞄准的是Bootloader在校验App签名“通过/失败”的那条条件跳转指令。经过几十次尝试确实能让固件在签名校验失败的情况下继续执行。虽然这种攻击需要仪器和耐心但它证明了任何单一安全机制都不是不可绕过的。防御侧我不建议完全从软件层面硬扛优先用芯片自带的硬件防护使能电压检测器BOR/POR和时钟检测器芯片检测到电压或时钟异常立即复位增加故障注入的难度。使用带错误注入防护的PUF或安全密钥存储让攻击者在物理篡改时密钥自动失效。关键比较操作使用恒定时间算法比如CRYPTO_MEMCMP这类硬件比较函数避免攻击者通过功耗曲线判断比较结果。侧信道攻击功耗分析、电磁分析主要针对软件密码实现。如果MCU有硬件加密引擎优先用硬件引擎必须用软件实现时应确保常数时间操作并引入随机时序扰动。这些措施做不到100%安全但能把攻击成本推到远超设备本身价值。4.2 调试接口与读保护一个JTAG口毁所有调试接口是所有MCU安全设计里最基础也最容易被忽略的一环。很多工程师开发调试时为了方便从来不锁调试口样机阶段没问题但量产时如果沿用同一个固件等于把芯片的整个内部存储都敞开给攻击者。无论上面做了多少层安全启动和加密只要调试口能连上攻击者可以直接停止内核、讀内存、导出Flash。以STM32为例读保护RDP分为Level 0、Level 1、Level 2三个等级Level 0无保护调试口完全开放。Level 1禁止通过调试口读取Flash但还可以连接调试器执行代码。Level 2彻底禁止调试口访问相当于熔断不可逆。量产设备应设置到Level 2。但这里有个代价一旦Level 2设置后续也无法在产线做固件级debug只能靠日志和应用层调试。所以正确的节奏是开发阶段用Level 0试产阶段用Level 1确认运行稳定后在最后一道产线工序切换到Level 2。我排查过一台发生“神秘故障”的设备工程师折腾了很久最后发现是产线编程器软件默认没有勾选“设置读保护”的选项导致每一台设备出厂时都处于Level 0状态。那批设备由于固件没有加密仿冒者大量抄板品牌方追查时才发现是这个低级失误。所以量产前的产线配置检查表里**“设定RDP等级并验证不可降级”**必须是红标项。4.3 升级回滚与降级攻击版本号也是安全边界有一种攻击很容易被忽略回滚攻击Rollback Attack。假设你的设备当前固件是2.0安全漏洞已经被修复攻击者却设法把固件降级到1.0而1.0存在已知漏洞于是攻击者再利用那个漏洞控制设备。这种事情在智能锁、ETC设备、工业控制器上都发生过。防御回滚攻击的标准手段是防回滚计数器Anti-rollback Counter。具体做法是在OTP或安全存储区域维护一个单调递增的版本计数器固件升级时新版固件携带的版本号必须大于当前计数器值Bootloader才会接受并更新。版本号一旦写入永不可回退。我把这个机制称为“版本号也是安全边界”——它可能不显眼但在攻击链里往往是最容易堵住的一条路。在部署防回滚时有一个细节需要提前考虑如果你的升级系统和OTA服务器不小心发布了错误版本导致版本号跳得很高后续所有旧版固件都无法再安装修复起来必须重新烧录。所以不要把版本号等同于UI上看到的“固件版本”最好在App内部使用一个独立的、单调递增的安全版本号字段并和业务版本号解耦管理。这样即使业务版本号需要回滚安全版本号依然可以保持单调递增。4.4 常见安全失效问题速查表问题现象可能原因排查思路解决方案固件可以随意被第三方烧录调试接口未锁定或公钥可覆盖检查RDP等级和OTP配置量产工序锁定Level 2公钥写入OTP并锁定设备被重放之前的合法指令协议无序列号或Nonce抓包分析指令时序增加Nonce和序列号窗口校验设备从OTA服务器下载升级包失败签名校验不通过检查App加载地址和签名范围配置统一Flash分区和链接地址重新签名攻击者通过功耗分析提取密钥软件密码实现非恒定时间审查AES/ECDSA实现切换到硬件加密引擎升级后旧固件仍能运行未实现防回滚固件包版本与安全版本号未绑定增加单调递增的安全版本号排查安全问题时要养成一个习惯先确认“设备当前暴露面”比如调试口是否打开、日志是否打印敏感信息、测试代码是否残留再看“密码学层是否有漏洞”最后才考虑“业务逻辑层有没有问题”。大多数实际案例都不是因为密码算法被破解而是因为某个基础环节没封住。5. 选型与资源不同档位MCU的安全方案怎么配5.1 从8位到32位安全能力分级并不是所有项目都需要换到带TrustZone的MCU。我整理了一个能力分级表你可以按实际情况对号入座芯片档位安全能力典型器件适用产品局限性8位低成本MCU软件AES、简单Flash读保护中颖、STC8、PIC16电子玩具、低端传感无安全启动密钥易提取主流32位MCU安全启动、硬件加密、Flash读保护STM32F4/G0、GD32E智能家电、传感器节点无TrustZone安全隔离有限中高端安全MCUTrustZone、安全存储、抗故障注入STM32H5/U5、LPC55xx、RA6智能门锁、工业控制、车载成本高开发门槛高专用安全元件/HSM防篡改、安全密钥存储、隔离密码运算ATECC608A、SE050、HSM模块高价值设备根密钥保护需要独立通信链路线我经常建议的项目组做选型时先把威胁模型和产品定价写清楚然后再看芯片档位。举个例子一个成本控制在20元以内的插座就不可能指望它抵抗物理攻击它的安全目标是防止普通用户抄板仿冒所以主流32位MCU的读保护加安全启动就够了。但如果是1500元的智能门锁面对的是职业小偷和破解团队那么堆上TrustZone和安全元件都不过分。5.2 开源与商业安全框架怎么选MCU安全生态目前已经有一些成熟的开源组件可以直接用不需要什么都从头写MCUboot开源安全启动和固件升级框架支持多平台是目前很多MCU SDK默认集成的Bootloader方案。TF-MTrusted Firmware-M基于TrustZone的ARM安全固件框架提供了安全启动、安全存储、Crypto服务、初始信任根等模块接口对齐PSA API。mbedTLS / PSA Crypto密码学算法库和接口规范提供了从TLS到对称/非对称算法的完整实现。厂商SDK方案意法半导体的STM32Trust、恩智浦的EdgeLock、瑞萨的Secure Crypto等都是集成度很高的商业方案开箱即用文档齐全。我在选择时会有一个原则框架能覆盖的尽量用框架但关键密钥管理和签名流程必须自己亲手审查。开源框架的代码质量通常不错但它不一定适配你的具体MCU型号和安全配置比如MCUboot默认的Flash分区策略可能和你的外部Flash型号不兼容TF-M的内存布局也需要根据芯片的SRAM大小重新调整。不要盲目相信“默认配置等于安全”它顶多等于“默认配置能跑起来”。6. 一些踩坑后的个人体会项目做得越多越觉得MCU安全不是某一个算法或者某一个芯片特性而是一条覆盖设计、开发、产线、运维全流程的链路。下面是我这几年实践下来最深的几个体会。安全设计必须从立项阶段介入不能等硬件定型、软件快投产后才说“加个加密吧”。我之前参与的一个项目因为驱动开发时没有预留认证数据存储区后期想加安全启动被迫调整Flash分区结果整个通信协议和升级逻辑都要改本来一周能完成的事拖了一个多月。反过来另一个项目在方案阶段就定义了密钥管理和产线注入流程落地非常顺利安全特性和普通功能一样按期交付。密钥注入流程要提前和生产部门沟通。研发和产线对安全的理解经常有非常大的偏差研发觉得“把密钥文件发给工厂就行”产线担心“烧录速度跟不上节拍”。所以安全方案里一定要包含产线落地工具、密钥分级逻辑和效率预估。最好在试产之前就完成产线烧录流程的验证而不是拖到量产前一天。安全测试不是只在实验室做一次渗透测试而是要持续做Fuzzing、故障注入、回滚攻击、调试口探测等方面的测试。安全是一个动态对抗的过程芯片固件和应用在不断更新攻击手法也在演进。如果你只在发布前测试一次那只能证明“发布那一刻是安全的”无法证明半年后依然安全。我给做物联网产品的朋友的建议是建立一份“安全回归清单”每次固件发版之前至少跑一遍包括读取保护状态检查、签名校验测试、升级回滚测试、非法调试请求测试。最后如果你只是想做一颗低成本MCU的简单联网设备至少在固件里把这几件事做对不要硬编码密钥、及时锁定调试口、升级包加上签名校验、通信协议带上防重放字段。做完这四件事你就已经超过了市面上相当大一部分不够重视安全的连接设备。在这个万物互联的时代安全不是选做题而是必修课。踩过坑之后回头看你会发现真正昂贵的从来不是加几项安全特性而是出了安全事故之后的善后成本。