免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

嵌入式固件安全指南:从安全启动到IP保护的防抄板实战

嵌入式固件安全指南:从安全启动到IP保护的防抄板实战 做嵌入式产品这行越到后期越会发现一个扎心的事实你辛辛苦苦调通的固件在别人手里可能只是一晚上的逆向工程素材。前两年我接手过一个工业控制器的项目客户反馈市场上一夜之间冒出了外观一模一样、功能也差不多的“兼容板”价格只有我们的一半。拆机一看主控芯片丝印被磨掉但里面跑的固件直接提取出来反汇编核心算法逻辑和通信协议几乎一比一复制。说白了对方就是把我们的Flash读出来简单分析后直接抄作业。从那以后我开始认真对待Embedded Security和IP Protection这两个词也踩了不少坑借此把整套思路和实操经验整理出来。这篇内容适合正在做嵌入式产品、尤其是对固件安全和知识产权保护有需求的工程师。不管你是做IoT设备、工业控制器、消费电子还是车规级ECU核心思路都能复用。1. 先把威胁模型讲清楚到底在防谁、防什么网上聊嵌入式安全很多人一上来就谈加密算法、安全启动、TrustZone但我想先劝你冷静一下。安全方案的选型取决于你的威胁模型。你用AES-256加SHA-512把整个系统武装到牙齿结果对方只需要一把逻辑分析仪挂在SPI Flash边上就能读到明文数据那你的密码学功夫就全白费了。1.1 最常见的四类攻击路径结合我自己的产品被抄的经历嵌入式设备的IP泄露通常来自四个方向离线静态攻击直接把Flash芯片拆下来用编程器读取固件镜像再通过binwalk、Ghidra、IDA等工具做静态分析。这是最低成本、最高收益的攻击方式当年我的产品就是这么被搞的。调试接口攻击开发阶段留下的JTAG/SWD接口没有禁用攻击者用J-Link连接目标板直接通过调试器读取内存、dump固件、甚至单步调试你的代码。在线动态攻击在设备运行过程中通过探测总线信号、监听通信报文、故障注入比如瞬间拉低电源电压、干扰时钟来获取敏感数据或绕过安全校验。物理剖片攻击使用FIB聚焦离子束、探针台等半导体分析设备对芯片内部走线进行物理探测。这类攻击成本极高一般军用级产品才需要重点考虑。1.2 确定你的“防御等级”这里有个简单的分级方法先把你的产品按照“被抄之后的损失程度”划分等级。比如一个开源创客板固件被读走完全无所谓甚至鼓励大家学习一个卖license的商用协议栈固件被抄意味着直接经济损失而一个车规级ECU、医疗设备或工业安全控制器固件被篡改甚至可能带来人身安全风险。等级不同投入的资源天差地别。创客板可能只需要一个读保护位就够了商用产品需要安全启动加固件加密高安全等级产品可能要配备独立安全芯片、HSM硬件安全模块甚至物理防护层。我自己的经验是先把最便宜、最有效的手段全部用上再评估是否需要上硬件安全芯片。很多项目其实连第一步都没做到位就急着谈高大上的方案。2. 安全启动链系统安全的地基工程如果你只打算做一项安全加固那我建议优先做Secure Boot。安全启动本身不直接保护IP但它保证了你的系统只能运行你签名的固件这从根上杜绝了非法固件运行和篡改注入。2.1 信任根Root of Trust是怎么建立的安全启动的核心是一个“信任链”。芯片上电后第一段代码是固化在芯片内部ROM里的BootROM这部分代码出厂时就被芯片厂商写死无法修改天然可信。BootROM的第一件事是读取eFuse一次性可编程存储中的密钥哈希或公钥然后用这把密钥去校验下一级Bootloader的签名。校验通过才把控制权交出去签名不匹配直接拒绝启动。Bootloader再去校验内核或应用程序固件一层一层像链式反应一样最终保证整个系统镜像都来自可信源头。打个比方你进一栋大楼门禁卡是第一道验证进电梯要刷楼层权限是第二道进办公室还要指纹打卡是第三道。哪怕有人复制了门禁卡没有后面的验证也进不到核心区域。2.2 密钥管理一把钥匙开一把锁还是层层穿透这里有一个常见的认知误区很多人以为固件里存了私钥就是安全的实际上私钥一旦被提取出来整个信任链就崩了。正确做法是私钥永远只存放在安全的地方设备端只保留公钥或公钥哈希。以我用过的NXP i.MX系列为例其High Assurance BootHAB流程中eFuse里存放的是SRKSuper Root Key的哈希值而不是SRK本身。真正的SRK私钥保存在你的安全服务器或HSM里用于对固件进行签名。设备端校验固件签名时只需要用SRK公钥解密签名值比对哈希即可。密钥的存储要分级用于签名的私钥放在离线的、有物理安保的机器里永远不联网。设备端公钥/哈希烧录到eFuse一次性写入后无法修改防止被替换成攻击者自己的密钥。通信层密钥如TLS私钥可以放在安全元素或TEE里与应用层分离。另一个很容易被忽略的点是密钥必须要有轮换机制和撤销机制。万一你的签名私钥泄露了你需要有一种方式让市场上的设备拒绝使用旧密钥签名的固件。这就涉及到密钥版本号和回滚保护需要在BootROM阶段就设计好。2.3 失败模式处理启动失败不要轻易“放行”调试安全启动时最烦人的问题是“为什么我的固件签名后启动不了”。很多开发者图省事在验证失败的分支里直接放行让系统继续跑美其名曰“兼容性”。这么做等于亲手把安全启动的锁打开了。正确做法是校验失败时能进入恢复模式比如通过USB下载固件就进入恢复模式不能恢复就直接死循环或关机。不要提供任何绕过校验的后门哪怕这个后门你觉得“只有我知道”。在安全领域隐藏式后门就是埋雷。3. Flash固件保护让抄板者无米下锅安全启动解决的是“改你固件的人”的问题但还没解决“读你固件的人”的问题。如果你的Flash内容直接明文存储攻击者不需要在意签名直接把固件提出来分析就行。我当年产品被抄就是对方直接把SPI Flash里的.bin文件拖出来分析。3.1 读保护性价比最高的第一道防线绝大多数mcu和MPU都提供了不同级别的调试/读保护功能。以STM32为例RDPRead Protection分为Level 0、Level 1、Level 2三个级别。Level 0是完全开放Level 1下调试接口无法访问Flash内容但SRAM和寄存器仍可访问Level 2是彻底锁定任何调试访问都不行而且不可回退。很多人以为开了Level 1就万事大吉其实不是。Level 1状态下的芯片如果攻击者把你的代码拿到另一个同型号芯片上跑他可以通过分析运行时的行为来逆向算法。更严重的是STM32的Level 1在某些情况下可以通过特定电压故障注入等方式降级。所以如果你对成本不太敏感直接上Level 2最省心。不过要注意一旦升到Level 2你自己也无法再通过调试器烧录和调试。建议在产品开发完成、测试稳定之后再在产线刷入Level 2的固件。3.2 固件加密就算dump出来也是一堆乱码有些芯片的Flash加密是硬件自带的比如NXP的LPC55系列支持Flash加密密钥存放在芯片内部读取Flash时自动解密。如果你用的主控芯片没有硬件加密可以考虑在固件层面自己做一层加密。我了解过比较好的实践是在固件生成后的镜像阶段做加密启动时由解密程序在内存中解密运行。加密算法优先选AES-CTR模式因为它可以并行处理解密速度快密钥不要硬编码在一个地方可以拆成多段分散存放或者由芯片的唯一IDUID派生得到。这里要特别注意一个坑不要只在固件层做加密就不做完整性校验。攻击者不需要知道你固件里的数据是什么他完全可以把你的加密固件原封不动地写回Flash做重放攻击。加密签名或MAC要结合使用一方面防泄露另一方面防篡改。3.3 芯片选型阶段的考量如果你还在选型阶段建议把以下能力作为硬指标芯片是否支持安全启动BootROM是否有硬件签名校验单元。是否有安全的Key Storage比如eFuse、OTP、安全元件接口。是否有硬件加密引擎AES、RSA、ECC加速器用软件实现加密在某些场景下慢到不可接受。调试接口是否支持永久禁用。是否有TrustZone或其他隔离执行环境。坦白讲这些能力在8位单片机上基本没有在Cortex-M系列上部分支持在Cortex-A系列和MPU上支持得比较完整。选型时不要光看主频和Flash容量安全特性在关键时刻能救你一命。4. 运行时防护与调试封锁别把后门留给对手静态加固做得再好如果运行时被攻击者钻了空子照样功亏一篑。调试接口是重灾区很多人开发完忘记关掉JTAG/SWD结果就是攻击者可以直接用调试器挂在板子上读写内存甚至可以把你芯片上运行的程序在线修改。4.1 调试接口的三种关闭方式不同平台的关闭方式不太一样但核心思路是一致的Fuse烧断方式通过烧写eFuse永久性关闭调试接口。这种方式不可逆适合量产前的最后一步。配置寄存器方式在系统启动代码中尽早配置调试接口为GPIO模式或禁用状态。这种方式可逆但也容易被攻击者通过修改启动参数跳过所以只适合“防君子不防小人”。认证方式比如ARM CoreSight的SecureJTAG需要先通过认证握手后调试接口才开放。这种方式兼顾开发和量产后的调试需求是比较推荐的折中方案。我自己的建议很简单产品定型后能永久关闭就永久关闭。真要留调试口也要加物理跳线或密码认证不要裸奔。4.2 侧信道攻击与软件对策侧信道攻击听着很高端实际门槛比很多人想象的低。最典型的是功耗分析Power Analysis。你在跑密码运算时电流波形会随处理的中间值变化攻击者用示波器采几条功耗曲线用统计分析就能还原出密钥。应对侧信道攻击软件层常规手段包括掩码Masking在计算过程中引入随机数把中间值与真实数据做逻辑运算掩盖起来。隐藏Hiding在计算流程中插入随机延时、伪操作破坏功耗曲线的对齐。算法层面优先选用在硬件上自带防护能力的密码引擎很多芯片的AES模块在设计时已经做了功耗平坦化处理。不过说实话对于大多数商业产品攻击者未必会上升到功耗分析这个级别。我更建议把精力放在前面说的调试接口封锁、Flash加密和边界检查上这些基础项做好了已经能挡住90%的业余选手。4.3 运行时完整性校验与反调试如果你的固件已经被加密、签名但攻击者仍然可以借助特定的漏洞比如缓冲区溢出、格式化字符串漏洞往内存里注入代码。此时可以考虑增加运行时的完整性校验比如周期性计算关键代码段的哈希值与预期值比对不一致就复位或进入错误状态。反调试方面可以检查调试寄存器、检测断点指令、检查执行时间是否异常等。但这些手段都会增加系统复杂度和不确定性我见过有些团队加了反调试后现场设备莫名复位查了半天发现是反调试代码误判了正常执行环境。要把反调试的误报率控制住不然等待你的就是不可预测的售后故障工单。5. IP保护与代码混淆之外更硬核的防抄板措施说完固件安全再聊聊更贴近“知识产权保护”的话题。很多场景下你不仅要防止固件被篡改更要防止整个产品方案被简单复制。5.1 代码混淆提高逆向成本但不解决根本问题代码混淆是在编译层面把代码结构打乱让反汇编结果难以理解。常见的混淆手段包括控制流平坦化、虚假控制流、变量名混淆、字符串加密、指令替换等。业界有不少工具比如OLLVM、Hikari、Tigress等。但需要清醒认知混淆只能提高逆向分析的时间成本不能阻止逆向。一个经验丰富的逆向工程师面对混淆代码的第一反应不是逐行读而是动态调试、抓运行时行为。所以混淆适合作为辅助手段不要作为唯一的安全措施。5.2 认证芯片方案把关键算法密钥锁在“保险箱”里一个性价比非常高的IP保护方案是把核心算法或License校验逻辑放到外部认证芯片里。主控MCU启动时通过加密协议与认证芯片握手得到正确响应才继续运行关键功能。市面上常见的有Microchip的ATSHA204A、ATECC608系列NXP的SE050系列等。这些安全芯片内置了硬件密钥存储和加密引擎密钥无法被外部读取攻击者即使完整复制了你的主控Flash也无法在没有安全芯片的情况下运行你的产品。我举一个实际发生过的场景客户的产品里有一段核心算法如果把算法直接放在主控固件里攻击者dump固件后用Hex-Rays反编译核心逻辑基本就暴露了。后来我们把算法的输入输出封装成一个服务密钥存在ATECC608里主控通过I2C发送计算请求安全芯片返回结果。攻击者即使拿到了主控固件看到的也只是一堆“看不懂的通信协议”。这个方案的核心难点在于主控和认证芯片之间的通信协议要自己设计好防止被中间的探测工具直接模拟回放。建议引入随机挑战数以及每次会话的临时密钥防止重放攻击。5.3 供应链安全别让你的生产环节成为泄密口IP保护不仅是个技术问题也是管理问题。我见过一个案例某家公司把固件和密钥直接发给代工厂烧录结果代工厂的一名员工把固件打包卖给了竞争对手。这已经不是技术能解决的范畴。建议做的事固件镜像在交付给代工厂前先加密产线上配备安全烧录器在烧录过程中由烧录器完成解密和密钥注入。不要把私钥或明文固件通过邮件、网盘等渠道传来传去。对涉及密钥和核心固件的员工签订明确的保密协议并限定访问权限。核心算法尽量拆分为多个模块不同的产线或代工厂只接触其中一部分降低整体泄露风险。6. 实际项目落地清单与坑位复盘最后分享一份我在项目中实际使用的加固清单和踩坑记录希望能帮你少走弯路。6.1 一个可复用的加固清单以我最近做的一个基于Cortex-M7的仪器仪表产品为例最终落地的加固措施如下安全层具体措施成本/难度启动安全使能芯片Secure BooteFuse写入SRK哈希签名固件低/中存储保护RDP Level 2Flash内容启用硬件加密低/低调试封锁量产固件永久禁用JTAG/SWD低/低运行时防护MPU配置可执行区域为只读关键函数CRC周期校验中/中IP保护ATECC608存储密钥主控与安全芯片双向认证中/中产线管理固件加密交付产线安全烧录中/中这套组合拳打下来抄板成本从“几百块的编程器加一晚上分析”上升到“需要专业硬件安全团队和芯片逆向设备”绝大多数竞争对手会直接放弃。6.2 遇到的坑和解决办法坑一开了安全启动后OTA升级失败导致设备变砖。原因很简单OTA下载的固件没有正确签名或者签名使用的密钥与eFuse里的哈希不匹配。解决办法是把签名流程集成到CI/CD里每次构建自动签名同时升级时先校验再写入固件写入完成后还要做一次回读校验确认无误才切换启动标志。坑二Flash加密后量产烧录速度慢到无法接受。原本用J-Flash烧录一个2MB的固件只要几秒开了加密后因为要动态解密/加密速度掉了好几倍。后来换了支持硬件加密的烧录器并通过并行烧录多颗芯片的方式把产线节拍提了上来。坑三反调试代码误报导致设备死机。我曾在固件里加了执行时间检测发现某次现场设备不定时重启查了几天发现是中断延迟导致主循环时间超阈值被反调试代码误判为“被调试”。最后调大了容差范围并且只在特定安全关键任务里做时间检测。坑四eFuse烧断后无法再调试后悔都来不及。这应该是所有做安全的人最容易犯的错。建议流程是开发调试阶段不烧eFuse只通过调试器配置寄存器的方式模拟安全启动流程小批量试产时用“可逆的调试开放”方式确认万无一失后量产版再烧eFuse彻底锁定。6.3 一个容易忽略的细节日志与错误信息的泄露攻击者除了从固件本身的二进制里找漏洞还会从设备的运行日志里找线索。如果你在代码里写了大量调试日志尤其是打印密钥、地址、内部变量内容的日志这等于给攻击者递刀子。建议量产固件关掉调试日志或者把日志级别提高到ERROR以上。如果必须要保留远程日志对日志内容做脱敏处理避免泄露内存地址、文件路径、协议内部字段名等信息。这半年我自己越来越深的一个体会是嵌入式安全没有绝对的安全只有成本与收益的平衡。你的目标不是造一个无法攻破的系统而是把自己的产品变成一个“性价比足够低”的攻击目标。当你把常见漏洞都堵上、把常规攻击路径都封死攻击者自然会转向更容易的目标。这套方法论我已经在多个项目里验证过希望对你也有用。
返回列表