嵌入式安全防护到底该怎么做——从硬件到固件的层层防线

📅 2026/7/1 2:40:04
嵌入式安全防护到底该怎么做——从硬件到固件的层层防线
三年前接了一个工业控制器的项目客户说安全你看着办。结果两个月被人通过UART拿到了root shell。不是黑客多厉害是我们根本没锁调试接口。这事让我老实了。后来花了大量时间补这方面的功课慢慢摸清了嵌入式安全到底在防什么、怎么防。嵌入式系统面对的是什么样的威胁和PC或云服务器不同嵌入式设备有一个致命问题攻击者物理上能摸到你的板子。PC被物理接触通常意味着你已经输了但嵌入式设备经常部署在无人场所——野外基站、路边的监控、工厂流水线的控制器。攻击者可以焊线、接逻辑分析仪、甚至用热风枪吹掉芯片封装。所以嵌入式安全分两大块物理安全防止通过硬件接口读取或篡改和固件安全防止通过软件漏洞提权或篡改。只做加密不做物理防护等于把保险柜钥匙贴在柜门上。第一道防线锁死调试接口最基础但也最容易忽略的一步。大部分MCU出厂时JTAG/SWD调试接口是默认开启的。产品定型后量产阶段就应该熔断fuse位来永久禁用调试接口。以STM32为例设置RDPRead Protection到Level 2HAL_FLASHEx_OBProgram(OBInit); // 设置RDP Level 2Level 1还能通过全局擦除解除Level 2直接把调试接口永久锁死J-Link连上去也识别不到内核。有些板子量产时没锁又要现场升级怎么办折中方案是在bootloader中做身份验证只有经过签名的升级包才开启临时调试通道。说句实话我们有一批产品就因为先发出去后面再补一直没补上。最后花了两倍时间做OTA安全升级全覆盖。研发阶段的决策会在量产时加倍奉还。第二道防线加密存储与安全元件光锁调试接口不够。攻击者还可以拆下Flash芯片用编程器直接读内容。很多SPI Flash的数据引脚在PCB上走线清晰示波器一搭就能抓到通信波形。解决方案有三个等级。第一级MCU内部Flash加密密钥存在OTP区域上电自动解密。读出来的全是密文。但密钥最终还是会在内部总线上传输理论上激光切割封装、探针接总线是能读到的。第二级外置安全元件SE。SE是一颗专用芯片密钥永远不离开它。你要做签名运算把数据发进去它返回签名结果。中间人即使监听了总线也只能看到请求和结果拿不到密钥。第三级是物理不可克隆函数PUF近三四年才在工业级芯片上普及。利用硅晶圆制造过程中的物理差异生成唯一指纹。同一版设计、同一批晶圆每颗PUF值都不同。有人开盖探测PUF值立刻改变密钥自动销毁。NXP的i.MX RT系列和Microchip的CryptoAuthentication系列都支持价格已经降到了工业级可承受的范围。第三道防线安全启动与固件签名安全启动Secure Boot的核心理念很直观芯片只运行经过你签名的固件。流程是芯片上电后BootROM先运行校验第一级loader的签名然后逐级向上。每一级持有上一级的公钥签名算法一般用ECDSA或RSA-2048。私钥必须离线保管——我看到很多团队直接把私钥放Git仓库等于没有签名。用TF-ATrusted Firmware-A生成密钥和签名openssl ecparam -genkey -name prime256v1 -out bl32_ecdsa_priv.pem openssl dgst -sha256 -sign bl32_ecdsa_priv.pem -out bl32.bin.sig bl32.binBootROM里烧录了对应的公钥哈希运行时逐级验证。任何一个环节签名不匹配芯片直接进入不可恢复的异常状态。不过Secure Boot有一个容易翻车的点公钥哈希烧进芯片OTP之后就没法改了。有次我们在开发阶段忘了烧录公钥等板子贴好回来才想起来。最后飞线到另一个样品重新走了一遍完整的密钥生成流程。第四道防线通信安全与TLS的嵌入式实现很多物联网设备直接把传感器数据用明文发到云端。更离谱的是我们审计过一个项目——UDP广播不加密、不校验、不认证。嵌入式设备做TLS最大瓶颈是资源。一颗Cortex-M0跑mbedTLS做一次完整的TLS 1.3握手大约需要3到5秒内存吃掉20KB左右。优化方向有三个。第一种会话复用第一次做完完整握手后用session ticket恢复后续只需一次往返。第二种PSK省掉证书交换和密钥协商只做对称加密。第三种用DTLS如果设备走UDPDTLS握手有丢包重传机制比硬套TCPTLS靠谱。我们最终选了mbedTLS加会话复用重连时间降到300毫秒以内。第五道防线固件更新与回滚防护一个有安全意识的设备要能安全更新固件、还要防止攻击者把固件回滚到有漏洞的老版本。回滚攻击的原理厂商发布了v1.2.3修复了一个远程代码执行漏洞攻击者拿到旧版固件包给设备刷回有漏洞的版本然后利用已知漏洞攻入。解决方案是在固件更新包中嵌入一个单调递增的版本号。Bootloader刷写前检查两条新固件签名是否有效、版本号是否大于等于当前版本。typedef struct { uint32_t magic; uint32_t version; // 单调递增 uint32_t size; uint8_t signature[64]; // ECDSA签名 } firmware_header_t;版本号存在OTP区域一次只能写入更大的值。还有一个更激进的做法是把版本计数器分开存——bootloader区域一份、application区域一份防止其中一份被篡改时另一份还能兜底。一个容易被忽视的角落侧信道攻击物理安全和加密都做了之后还有一类叫侧信道攻击——不破解算法而是观察芯片在加密运算时的功耗波形、电磁辐射或运算时间。AES的计时攻击就是典型加密不同字节时MCU消耗时间有差异采集上千次时序就能推导出密钥。防护手段包括恒定时间代码、功耗加扰和随机延时插入。大部分厂商的硬件加密引擎已经内置了这些防护。对安全等级要求高的话尽量用硬件引擎自己手写的AES在侧信道面前不堪一击。回到最初那个项目那批被攻破的控制器最后做了固件更新。新版本锁了JTAG、加了Secure Boot、通信改用mbedTLS加密、固件升级加了签名和版本校验。前后改了三个月。安全这件事在嵌入式领域不是要不要做而是从哪个阶段开始做。原理图阶段定好带安全特性的芯片、Layout留好安全元件的位置、量产前定好密钥管理流程——每一步都比事后打补丁省十倍成本。那个当初说安全你看着办的客户第二年续约时把安全要求列了整整两页。