news 2026/8/26 6:45:09

故障注入攻击全面解析:从威胁模型到纵深防御实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
故障注入攻击全面解析:从威胁模型到纵深防御实践

1. 故障注入攻击的威胁模型与攻击面分析

1.1 从一次“意外”谈起:为什么故障注入值得被认真对待

先讲个我早期经历的事。那时我在做一款安全支付终端的固件开发,产品已经进入量产前最后一轮测试。硬件组同事在实验室里给主控芯片做电压波动测试,原本只是常规的信号完整性验证,结果测试设备在某个特定瞬间产生了一个极窄的电压毛刺,设备竟然跳过了Secure Boot的签名校验环节,直接进入了主系统。当时大家的第一反应是“硬件bug”,但做安全审计的同事立刻警觉——这不是普通的电气干扰,而是一类被称为fault injection attacks的攻击行为被无意间触发了一次。

那次之后,我对故障注入攻击的理解发生了根本转变。它不是发生在抽象加密算法层面的数学攻击,而是发生在物理世界里的真实干扰。攻击者不需要破解你的算法,不需要知道你的密钥,只需要让芯片在某一瞬间“算错一次”,安全边界就可能被撕开。这个认知,是构建一切防御方案的起点。

故障注入攻击的本质,是在芯片正常执行过程中人为引入瞬态错误,让处理器、存储单元或外设产生违背设计预期的行为。最常见的目标包括:跳过认证检查、绕过安全启动、降低加密轮次、篡改控制流、提取密钥材料。这类攻击的可怕之处在于,代码层面的算法强度再高,如果底层硬件在执行时被“物理黑客”改写了状态,一切防御都可能形同虚设。

1.2 攻击者的目标与攻击面的分布

要把防御做扎实,必须先搞清楚攻击者想打哪里、能打哪里。我在实际项目中会把攻击面按“数据通路”和“控制通路”进行区分,这两条路径在故障注入下的表现完全不同。

数据通路上,攻击者希望篡改正在处理的数据本身。比如在RSA签名计算中注入故障,让模幂运算的中间结果出错,就可能通过错误签名反推私钥。教科书上经典的Bellcore攻击和Boneh-DeMillo-Lipton攻击,核心思路都是让签名计算产生错误结果,再对比正确与错误输出,从数学上恢复密钥。这类攻击最有效的注入目标是存储运算结果的寄存器、SRAM、Cache,或者代数协处理器。

控制通路上,攻击者希望改变程序执行的走向。典型效果是“跳过关键跳转指令”,让条件判断永远为真或永远为假。比如固件中有一段“if (verify_signature() == FAIL) return;”的逻辑,攻击者如果在verify_signature()返回值写入寄存器的那条指令附近注入故障,就可能让CPU拿到一个错误的“SUCCESS”返回值,进而绕过认证。密码输入验证、启动流程检查、权限判断、许可证校验,这些统统是控制通路上的高危目标。

从攻击者的角度看,还有一个更省力的思路是针对芯片生命周期状态做手脚。许多安全芯片有OTP(一次性可编程)存储区、efuse阵列、安全状态寄存器,用来记录“是否已经完成初始化”“是否处于调试模式”“安全等级是多少”。攻击者注入故障把这些位翻转,就可能把芯片从安全模式拉回调试模式,或者让OTP写入校验失效。这块在实际产品中最容易被忽略,因为硬件状态的管理通常由厂商SDK封装好,固件工程师很少会深究其物理实现。

1.3 现实攻击者画像:威胁建模的出发点

做防御设计时,不能拍脑袋决定花多大成本,必须先对攻击者做分级画像。我通常把防护对象分成三类:

第一类是机会型攻击者,手里只有市场上买得到的通用设备,比如稍微改装过的SMD贴片机探头、电磁脉冲枪、廉价信号发生器和简单的电压毛刺发生器。他们的攻击能力有限,通常只会做简单的外部电压毛刺或时钟毛刺,不会做复杂的激光定位或芯片开盖。对这类攻击者,一部分硬件层面的基本防护就能拦住绝大多数尝试。

第二类是精通硬件攻击的实验室级攻击者,具备芯片开盖、FIB(聚焦离子束)电路修改、精密激光注入台、高速示波器和逻辑分析仪等工具。他们能对芯片做反向分析,找到安全Metal层走线,精确打击某个Flip-Flop。这类攻击者的威胁非常大,单一防御措施很难完全挡住,必须靠纵深防御和实时检测来对抗。

第三类则是国家级机构或顶级安全实验室,拥有芯片级全流程分析能力,甚至可能直接通过供应链植入后门。对绝大多数普通电子产品而言,这类攻击者的威胁模型超出了合理成本范围——但如果你在做金融安全芯片、车规域控制器或者政府级加密模块,那也不得不考虑。

在项目启动时,我会把攻击者画像写成文档同步给硬件、软件、结构设计三个团队,因为不同等级的威胁直接决定了后续的防护措施投入。比如只防机会型攻击者,可能加个电压监测芯片就够了;如果考虑实验室级攻击者,那就必须有冗余计算、运算随机化、内联检测逻辑这些组合拳。

2. 故障注入手段详解:攻击者是怎么“动手”的

2.1 电压毛刺注入:最容易上手的大门

电压毛刺(Voltage Glitch)是故障注入攻击里门槛最低、也最常见的手段。原理很简单:芯片工作电压都有个规定的容差范围,比如3.3V的IO供电,允许±10%波动。攻击者通过一个快速响应的MOSFET开关电路,在精确控制的时刻把供电电压短暂拉低到2.5V甚至更低,宽度在几纳秒到几百微秒之间,就能让芯片内部的逻辑门因为供电不足而产生时序违规,导致某些寄存器采样到错误值。

我的经验是,电压毛刺的攻击参数里最关键的三个变量分别是毛刺宽度、深度和注入时刻。毛刺宽度决定了影响的时间窗口,太窄可能影响不到关键指令,太宽容易让整个芯片直接复位;毛刺深度决定了逻辑出错的程度,太浅可能只是让非关键路径上的电路工作异常,太深会直接造成掉电;而注入时刻是真正的难点——攻击者需要知道目标指令大概在什么时间点执行,这通常需要通过侧信道分析或者经验试探来确定。

防御电压毛刺最常用的措施是片上电压监测。许多安全MCU内置了BOD(Brown-Out Detector)或专用的电压传感器,当检测到供电电压跌破阈值时立即产生复位信号。但这里有个关键细节:电压传感器本身的响应时间必须够快,如果传感器需要几十微秒才触发复位,而攻击者的毛刺只有几纳秒,很可能毛刺已经打完、故障已经注入成功,复位信号才姗姗来迟。选型时一定要看芯片数据手册里的电压监测响应时间,实测时也要用示波器验证真实行为。

2.2 时钟毛刺注入:把“时间”变成武器

时钟毛刺和电压毛刺思路类似,只不过干扰对象从供电变成了时钟信号。芯片内部的时序电路依赖时钟沿来触发寄存器采样,如果某个时钟沿来得太早,寄存器就可能采到还在传播途中的不稳定数据,产生建立时间(Setup Time)违规;如果某个时钟沿被删掉,指令又会重复执行或跳过。攻击者通常会在外部时钟线或者PLL反馈路径上注入一个极窄的额外脉冲,就能让CPU在某个特定周期“快进”或“卡顿”一次。

时钟毛刺的高明之处在于,它比电压毛刺更容易精确定位。因为芯片是同步时序系统,每条指令的执行周期数是固定的,攻击者只要数好从复位开始经过了多少个时钟周期,就能精准预测敏感指令(比如比较指令、跳转指令、密钥加载指令)何时执行。这也是为什么很多安全芯片会选择使用内部RC振荡器而不是外部晶振——内部RC振荡器的频率会随工艺偏差变化,而且不暴露给外部攻击者,让精确的时钟周期预测变得困难。

防御时钟毛刺,硬件上通常的做法是使用带监测功能的PLL,检测到参考时钟异常跳变就自动切换到备份时钟源;软件上则要尽量减少对外部时间的依赖,避免用固定周期数的循环去处理安全关键操作。我遇到过一种很有意思的软件缓解方案:把安全认证流程的指令执行序列故意加进随机空转指令(NOP + 随机跳转),让每条指令的执行周期变得不可预测,攻击者很难对准注入窗口。

2.3 电磁故障注入与激光注入:无接触的“外科手术”

电磁故障注入(EMFI)这几年在安全研究圈子里越来越火。攻击者用一个手工绕制的线圈探头贴在芯片上方,通过高压放电在探头中产生瞬态强磁场,强磁场在芯片内部感应出涡流,局部电压被扰动,从而影响附近逻辑电路的正常工作。它的优点是完全无接触,不需要对芯片做物理连接,所以非常适合针对封装完好、无法直接探针点测的产品。

EMFI的定位精度取决于探头的尺寸和位置。几毫米直径的线圈就能影响到一个功能模块,配合XYZ三轴移动平台,攻击者可以扫描芯片表面的不同位置来找敏感区域。我见过一些研究团队用步进电机加摄像头做自动化扫描,一个晚上就能画出芯片的“故障注入敏感度热力图”,哪些位置能跳过Boot代码,哪些位置能让AES解密出错,一清二楚。

激光注入则是精度最高的故障注入方式。激光照射到芯片衬底或金属层时会产生光生载流子,进而改变晶体管节点的电荷状态,相当于对特定存储单元做了一次“精确翻转”。配合高倍显微镜和精密位移台,激光可以精确定位到单个晶体管或者单个SRAM单元。但激光注入通常需要芯片开盖去封装,这让它的攻击门槛高了不少,主要出现在实验室级的攻击场景中。

2.4 温度、频率与组合攻击:别忽视“非主流”手段

有些攻击者会用极端温度把芯片推到工作范围边缘。低温会让芯片时序裕量变小,高频下更容易出错;高温则可能让存储单元的数据保持能力下降,导致SRAM或Flash内容翻转。虽然单靠温度很难实现精确的故障注入,但作为一种辅助手段,它能降低其他注入方式的成功率。比如攻击者先把芯片冷却到-40℃,再配合一个很弱的电压毛刺,可能本来毛刺深度不够造成故障,低温下就能轻易得手。

还有一种容易被忽略的组合攻击思路:先用软件漏洞或者调试接口进入一个低权限状态,再用故障注入跳过剩余的高权限检查。很多防御设计只考虑了系统在正常启动流程中如何对抗故障注入,却忽略了低权限状态下同样需要保持故障检测能力。我建议做威胁建模时,把“已进入到Ring 3/非安全态/用户态”之后还能用故障注入提升权限的场景也一并纳入分析——软件边界和物理攻击边界之间存在大量灰色地带。

3. 防御设计总体架构:纵深防御与多层级协同

3.1 为什么单点防护永远不够

我见过不少团队在早期做防护设计时,喜欢押注在“某个特别强的防护点上”。比如用了一颗号称“抗故障注入”的安全芯片,就认为万事大吉;或者写了一套复杂的状态校验代码,就觉得攻击者无隙可乘。但实际漏检案例几乎永远来自“没想到的组合”。

根本原因在于,攻击者拥有物理层面的优势。芯片内部的每一个电压域、每个时钟域、每个存储单元,对攻击者来说都是潜在的注入点。你的软件防护可能覆盖了CPU执行的路径,但电源管理单元、DMA控制器、总线仲裁器、DDR控制器这些地方同样可能被注入故障。单点防护做得再好,攻击者只要换个注入目标,就能绕开你的防线。

纵深防御(Defense in Depth)的核心思想,是让攻击者在每一层都要付出代价、承担不确定性。攻击者即便绕过第一层检测,第二层独立的检测机制还可能让他的注入结果无效;即便硬件检测全部失效,软件层面还有一致性校验在守着。每一层机制不要求100%阻断攻击,但它们的组合效应会让攻击成功率降低到实际不可利用的水平。

3.2 纵深防御的四层架构参考

我习惯把故障注入防御设计分成四个层级,从底层到顶层依次是器件级、硬件架构级、固件/软件级、业务流程级。每一级的防御效果可以独立评估,最终总体安全性由各级防御覆盖率共同决定。

器件级防御主要依赖芯片厂商的内建安全特性。包括电压/时钟/温度监测器、主动屏蔽层、片内传感器、安全存储、乱序执行、物理不可克隆函数(PUF)等。对应用开发者来说,这一层的选择相对被动——你只能通过选型来获取更坚实的底层防线,无法自行修改硅片实现。

硬件架构级防御是系统设计者可以掌控的部分。比如外部电路上的电压监控芯片、看门狗、双片冗余方案、总线宽度校验(ECC)、硬件安全模块(HSM)隔离、物理防拆保护壳等。这一层需要硬件工程师和安全工程师密切配合,要考虑的是“板级电路如何容忍和检测上层的异常状态”。

固件/软件级防御是大多数安全工程师投入精力最多的地方。包括控制流完整性检查、关键函数双执行、关键数据冗余存储、随机延迟与随机冗余指令、循环计数器、校验和/CRC、基于时序和功耗的异常检测等。这层防御的价值在于它不依赖额外硬件,可以在现有MCU上直接落地,而且可以根据实际攻击手段持续迭代。

业务流程级防御是最容易被忽视的一层。比如在安全关键操作(密钥生成、签名)中加入人工确认机制,在多步操作中加入状态机推进顺序校验,在安全芯片内使用一次性随机数作为会话标识来隔离重放和注入。这个层面的防护思路是:即使攻击者让某次操作在错误状态下执行成功,业务层面的状态约束也让它无法真正造成危害。

3.3 分层设计中的职责划分与接口约定

在一个实际项目中,防御设计最怕的是责任边界不清。我当时做过一个安全功能模块,“芯片内部SRAM错误检测”是器件级的事,“外部DDR的数据校验”是硬件架构级的事,“固件中关键变量的镜像校验”是软件级的事,“密钥轮换策略”是业务级的事。分工如果不明确,就会出现某个安全关键参数在四个层级之间拷来拷去,最后每一层都认为别的层做了校验,结果是每一层都没做完整校验。

我建议在项目设计文档中专门用一张表格列出“安全属性分配矩阵”,逐项标明哪些属性由哪个层级负责保障。比如:

安全属性器件级措施硬件架构级措施固件级措施业务级措施
电压异常检测内建BOD传感器外部电压监控IC启动时自检电压校准运行日志记录异常事件
时钟异常检测PLL失效监测外部看门狗时钟关键循环耗时评估时间戳连续性校验
关键数据完整性SRAM ECC校验外部Flash ECC/CRC双变量冗余+定时校验数据重建/重置策略
控制流完整性硬件返回地址堆栈保护MPU/TrustZone区域隔离关键函数CFI检查状态机推进顺序约束

这个表格的威力在于,每个团队都能清楚看到自己负责的“安全属性”,避免出现重叠负责但执行程度不一样、或者无人负责的真空区。表格评审完成后,各团队再围绕表格中的每项措施讨论具体的实施方案和验收标准,反复迭代直到所有安全属性都有明确的落地路径。

4. 硬件层加固实践:从选型到板级设计

4.1 安全MCU选型时的故障注入防护功能怎么挑

很多工程师选安全MCU时只关注CPU主频、Flash大小、外设丰富度,对故障注入相关的硬件特性关注不够。等到做故障注入评估时才发现芯片内置的防护能力远不够用,这时候只能靠软件硬补,代价非常大。我总结了选型时需要优先确认的几项关键特性,每一项都在实际对抗测试中证明过价值。

首先是电压监测器的数量和响应时间。一片合格的安全MCU至少要有两路以上独立的电压监测通道,一路监测核心供电,一路监测IO供电,而且响应时间要足够快。不少工业级MCU的BOD响应时间在几十微秒级别,听着很快,但对于纳秒级的毛刺注入其实谈不上可靠拦截。真正合格的安全芯片会提供“快速电压毛刺检测”引脚或内部事件,响应时间做到纳秒级,同时包含可配置的触发阈值。选型时直接向FAE要这个参数,很多厂商会支支吾吾,支支吾吾本身就说明他们没做过针对性设计。

其次是时钟监测器。安全芯片最好能同时接受外部晶振和内部RC振荡器,并且具有自动切换能力。故障注入攻击者一旦发现你在用外部晶振,最大的概率是直接在晶体附近做时钟毛刺注入,因为引脚裸露在外、信号容易捕捉。如果芯片检测到时钟频率异常,可以立即切换到内部RC并发出报警中断——这个特性在对抗时钟毛刺攻击时几乎是救命级别的。

然后是安全启动和调试保护。调试接口(JTAG/SWD)是故障注入攻击的最佳跳板。攻击者通过物理手段解除调试保护(比如用激光切断efuse的熔断检测电路),就能获得CPU的完全控制权。选型时要确认芯片是否支持基于密钥的调试认证、调试端口是否可以在量产阶段彻底永久关闭、efuse区域是否有防篡改检测。这里要特别提醒:关闭调试端口的操作一定要在产线上作为最后一道工序执行,我之前见过有工厂为了方便返修,把调试端口保留到软件发布流程的最后一刻,结果留下了大量的攻击面。

4.2 板级防护电路:外部器件如何补足芯片的盲区

芯片内置的防护再强,也不可能覆盖所有板级场景。我在板级设计中通常会加三类外部电路来增强故障注入抵抗力。

外部电压监控IC是必备项。选择响应速度快(纳秒级)、带阈值迟滞的监控芯片,放在主控芯片的供电入口处。它和芯片内部BOD的区别在于,它是独立于主控之外的“另一个裁判”,即便攻击者先让主控芯片内部某个传感器逻辑失效,外部监控IC依然能检测到异常电压并触发复位。复位信号最好直接连到主控的复位引脚,同时也要连一个GPIO让固件能感知到“发生过电压异常”。

如果把外部电压监控IC设计成独立于主控之外的“另一个裁判”,它还应该具备独立的供电来源或者至少独立的参考源,否则攻击者将整个电源域拉低时,监控IC自身也会失灵——好的设计会把监控IC的供电接到一个不易被同一个毛刺干扰的域,比如独立的LDO输出。

板级看门狗则是另一重保障。现在很多MCU自带看门狗,但都有个共同弱点:可以被固件关闭或喂狗时间可以被预测。我倾向于使用外部独立看门狗芯片,喂狗信号由固件中一个专门的安全任务控制,喂狗序列使用伪随机数生成,这样攻击者即使让主流程序卡死在某个循环里,也没法轻松模拟出正确的喂狗序列。外部看门狗超时后触发的动作不只是复位,最好是“复位+将安全关键信号拉到安全态”,比如关闭输出继电器、断开电机驱动、标记非易失存储区中的安全事件。

对于物理攻击防护,板级还能做的是“安全围栏”——用一组PCB走线包裹住敏感信号区域,走线连接到主控的中断引脚,一旦走线被切断或桥接就触发紧急处理。做这种围栏走线时,宽度、间距、过孔位置都要仔细规划,避免围栏本身成为天线把毛刺引入内部信号。我见过一个产品把围栏走线布得又长又宽,结果攻击者直接在围栏上方用EMFI探头打了一个强脉冲,围栏没报警,倒是内部信号被感应电压干扰了——这就很讽刺。

4.3 电源网络设计中的“抗毛刺”细节

电源网络设计对故障注入防御的影响,常常被很多团队当成“信号完整性”问题而非“安全问题”来处理,但实际攻击已验证过,不合理的电源设计会让本来很弱的毛刺攻击也奏效。

我在低噪声LDO或DC-DC的输出端会特别关注大容量去耦电容的布局。常规经验是每颗IC旁边放0.1uF高频去耦电容,主电源输入端放10uF~100uF的储能电容。但这只能平滑普通纹波,对几十纳秒的毛刺效果有限。想要真正抗毛刺,需要在主控芯片的电源引脚附近组合使用多种容值的电容,比如0.1uF、1nF、100pF各一只,形成宽频段的低阻抗路径。高频小电容离芯片电源引脚越近越好,最好压在PCB的背面正对电源脚的位置,这样毛刺能量在进入芯片之前就被旁路掉了。

还有一个小细节:电源走线宽度。有些人为了省空间,把电源走线画得很细,导致路径电感偏大。电感大的后果是毛刺电流通过时瞬间电压下降得更剧烈,相当于攻击者用同样的毛刺枪在你家地板上打出更深的坑。我习惯把安全关键芯片的电源走线做到能通过3倍以上额定电流的宽度,并避免在走线中间打过孔或者细颈,尽量保持完整平面供电。

4.4 封装与物理防护:要不要做开盖防护设计

对于那些有物理攻击威胁的产品,封装层面的防护值得纳入设计考虑。最高防护等级需要采用带主动屏蔽层的安全芯片,它在芯片顶层金属覆盖一层蛇形走线网格,任何试图用FIB或激光切断这条走线的行为都会导致安全逻辑触发数据擦除。带主动屏蔽层的芯片成本不低,但对金融终端、汽车T-Box这类产品来说是值得的。

如果芯片本身没有主动屏蔽层,退而求其次的做法是在PCB上覆盖一层防拆网格防拆胶。防拆胶(通常是环氧树脂)能让开盖难度大增,攻击者需要用热风枪长时间加热才能软化胶水,但这个过程中芯片温度异常、功耗特征变化明显,恰好能被电压/温度监测器捕捉到。网格设计则是在关键走线上方铺一层细密的地网络,任何物理侵入都会破坏这层网络,触发报警。不过要注意,网格只能防“开盖”,防不了“不接触的EMFI攻击”,所以它不能替代加密算法层面的抗注入设计。

5. 软件层防御:在固件内部构筑多道防线

5.1 关键数据冗余:让“翻转一位”失效

软件层面的故障注入防御,核心思路之一就是让单点故障注入的“性价比”降到最低。最简单有效的手段就是关键数据冗余存储。这里的“冗余”不只是存两份一样的值,而是要把原始值和校验备份放在不同的存储位置、用不同的编码方式。

我通常会把安全关键变量(比如认证状态、密钥索引、权限等级)按照“原值+反码镜像+CRC校验”的格式存储。每次读取时,将三份数据分别读出,验证原值与反码镜像是否互补、CRC是否匹配。任何一份数据被翻转都能被检测出来。更严格的设计是每隔一段时间(由RTOS的tick驱动或专门的安全任务驱动)主动执行一次全量校验,而不是等到使用时才检查——因为有些故障注入不直接改变当前行为,而是改变后续某条分支的判断条件,主动校验能及时发现潜伏的修改。

存储位置也有讲究。如果原值放在SRAM的固定地址,镜像就放在距离较远、甚至不同总线域的位置;如果原值在寄存器里,镜像就放在SRAM里。必须避免攻击者用一个宽范围激光脉冲把原值和镜像一并翻转。这个思路同样适用于Flash中的关键配置项,可以存储两份不同编码的副本,启动时做交叉校验。做这些校验的代码本身也需要提高抗注入能力——我见过有人辛辛苦苦存了三份冗余,结果校验代码只有一条if语句,攻击者把if语句跳过就前功尽弃了。

5.2 控制流完整性:让跳转命令不可被随意跳过

故障注入攻击最经典的战果就是“跳过一条关键跳转指令”,所以控制流完整性(CFI)检查在防御设计里位置极高。最简单的CFI实现是在关键分支之后立即放置校验变量

假设你有这样的代码:

if (authenticate_user(input) == AUTH_OK) { enter_privileged_mode(); }

攻击者只要在authenticate_user()返回后的比较指令上注入一个故障,让比较结果错误地判定为相等,就能直接进入特权模式。改进后的写法是:

uint32_t auth_result = authenticate_user(input); uint32_t auth_mirror = ~auth_result; if (auth_result == AUTH_OK) { if (auth_mirror != ~AUTH_OK) { fault_recovery(); } enter_privileged_mode(); }

多了一次校验,攻击者需要同时保证两处比较都出错,成功率立刻下降一个数量级。但这种方法要想再进一步加固,就得采用独立校验函数——让校验逻辑放在另一个编译单元、甚至不同的代码段中,避免攻击者通过分析二进制找到所有比较点一次性全部绕过。我之前做过一个项目,把CFI校验拆成了三个独立的函数,分布在不同的Flash页,每个函数之间插入随机延迟,实测下来,想精准定位并绕过全部校验点的时间成本翻了好几倍。

再进一步,还可以使用硬件辅助的CFI。ARM的TrustZone-M和最新的Cortex-M55系列提供了可编程的异常事件监测,可以在非法跳转发生时捕获异常。有些高端MCU甚至带硬件返回地址栈(Hardware Return Address Stack),防止攻击者通过故障注入改写返回地址。选型阶段如果确认需要应对高等级物理攻击,这些特性会是重要的加分项。

5.3 双重执行与结果比较:用算两遍来对抗“算错一次”

双重执行(Dual Execution)的防御逻辑非常朴素:对关键计算算两遍,比较两次的结果是否一致,不一致就报错。这个思路实现起来不复杂,但有几个地方需要特别注意。

首先是两次执行之间要有“随机隔离”。如果攻击者的故障注入窗口覆盖了两步操作(比如一个毛刺宽度很大,两次计算都被影响),那双重执行就失效了。所以我在两次执行之间会插入随机长度的延迟或者随机次数的空转操作,让攻击者很难用单一毛刺同时污染两次计算。有些芯片支持在两次计算之间切换时钟源或电压域,这种硬件级别的隔离更可靠。

其次是结果比较本身的抗注入能力。如果比较逻辑只有一句“if (result1 != result2)”,攻击者把这个判断跳过后双重执行就形同虚设。我的做法是:把比较结果作为后续安全操作的状态来源,而不是仅仅打印一条错误日志。比如在签名流程中,两次签名结果不一致时,不仅返回失败,还要让密钥寄存器立即进行自毁清零。这样攻击者即使绕过了比较,后续仍然会因为没有合法签名而卡住。

这里还要提一下“比较次数”的问题。有些设计只比较结果,但攻击者可以注入故障让两次计算产生相同错误结果。为了防止这种情况,双重执行比较的不仅是数值,还可以把中间状态也纳入比较——比如记录中间轮次的状态哈希。对于计算量允许的场景,可以把整个计算过程做成可验证的增量哈希链,任何一步被扰动都会导致最终校验失败。代价是性能开销,收益是非常可观的抗注入强度。

5.4 随机化与时间混淆:让攻击者抓不准时机

故障注入攻击对时序有极强的依赖——攻击者需要知道“在哪一条指令执行的哪个时钟周期”触发毛刺。因此,在软件执行流程中引入不可预测的时间和顺序扰动,是成本低但效果显著的防御手段。

我常用的技术包括:在安全关键函数入口插入随机数量的NOP指令,顺序由真随机数发生器(TRNG)驱动;将安全关键操作的执行顺序在多条等价路径之间随机切换,比如AES的S盒查表和位切片两种实现方式随机选择;在循环计算里加入随机的迭代顺序或随机的分块大小。这些手段的根本目的是提高攻击者的“瞄准难度”,当攻击者无法预知目标指令何时执行、在哪条路径上执行时,即便毛刺注入成功,故障落在安全关键指令上的概率也大幅下降。

时间混淆还有一个容易被忽视的价值:它破坏了攻击者对芯片行为的“可预测性建模”。很多自动化故障注入工具会先做一轮扫参(扫描毛刺宽度、深度、注入时间),找到最容易成功的参数组合,然后再批量精确打击。如果关键路径的执行时间每次都不一样,工具扫出的“敏感窗口”就无法稳定复现,攻击者需要反复重试才能偶尔命中一次,成功率大幅下降,从而更可能被检测机制发现。

5.5 安全启动流程中的故障注入防护细节

Secure Boot是所有安全固件的信任根,也是故障注入攻击的重点目标。攻击者的理想目标是让Bootloader跳过签名校验、跳到非授权代码或者加载错误版本固件。我在设计安全启动流程时,除了常规的逐级哈希链校验,还会叠加几层故障注入专用的防御措施:

启动时对Boot ROM的哈希值和启动状态寄存器做多次读取和交叉验证。Boot ROM是只读的,正常状况下每次读到的内容一致,但受到物理干扰时可能读到不一致的值。这里的做法是:连续读取3次,每次中间插入一个随机延迟,3次结果必须完全一致才通过。如果有一个字节对不上,直接进入死亡循环或强制进入Bootloader升级模式,而不是继续往下执行。

启动流程中的安全状态寄存器也要特殊对待。安全状态寄存器记录“当前是否已完成安全启动”这类信息,攻击者如果翻转它,可能在未完成校验时就获得高权限。我会把这些寄存器所有位都冗余存储一份镜像,每次状态迁移前先做一致性检查。状态迁移本身也要用状态机固化——不允许从“未验证”状态直接跳到“系统运行”状态,必须经过“校验中”和“校验完成”两个中间状态。

另外,Secure Boot中每一步的执行顺序不能是固定的。我在Bootloader中把几个独立校验模块的执行顺序随机打乱,依赖关系通过状态字而非物理顺序来管理。这样攻击者无法假定“第几个模块是签名校验”,大幅增加了定时瞄准的难度。

6. 密码算法实现的抗故障注入设计

6.1 为什么算法实现比算法本身更重要

在很多人的理解里,AES、RSA、ECC这类标准算法只要密钥长度够长就安全。但故障注入攻击正好打在这个认知的盲区上——算法本身的数学强度只解决了计算逻辑问题,而物理实现过程中出现的瞬时错误则完全绕开了数学强度

以AES为例,标准AES-128执行10轮运算,如果某次故障注入刚好让第9轮之后的MixColumns变换被跳过,攻击者拿一个正确密文和一个错误密文做差分分析,就能在极短时间内恢复出密钥。算法本身没有任何问题,问题出在“第9轮的计算结果被物理手段篡改”上。

也就是说,密码算法实现必须在逻辑正确之外,额外建立一组错误检测、结果验证机制,保证“即便算错,也算不出有用的错误结果”。

6.2 AES实现中的冗余与校验方案

我在物联网设备中最常落地的是AES-GCM,它自带认证标签,天然能发现密文被篡改。但认证标签在解密末尾才校验,如果攻击者在解密过程中注入故障,可能得到部分错误明文的同时标签校验还没来得及生效。所以更稳妥的做法是AES解密完成后,再用独立密钥做一次加密对比——如果解密结果正确,用同一密钥重新加密后应该得到原始密文。

这个方案的性能开销是两倍的AES计算,但对于安全关键操作(比如固件解包、通信握手)来说完全值得。还有一点要注意:用于对比的计算路径要和主路径保持隔离。如果两次运算用同一份S盒、同一段代码,攻击者既可以对主路径注入故障,也可以对验证路径注入故障。我见过一个实现,验证路径和主路径共用同一个S盒查找表,结果是攻击者把SRAM中的S盒表内容翻转了一个字节,两条路径同时出错,最终错误数据反而通过了校验。

更进一步的AES加固是拆分为独立的字节代换和列混淆阶段,在每个阶段之间插入CRC校验或奇偶校验。这类检测粒度更细,能更快发现故障发生的位置,但实现复杂度也更高。对于一般物联网产品,我会在“解密后回算加密”和“S盒表CRC校验”这两项中至少做一项。

6.3 RSA/ECC实现中的抗故障注入特殊手段

RSA和ECC这类公钥算法对故障注入特别敏感。RSA使用CRT加速计算时,如果模指数运算中任何一步出错,攻击者通过错误的签名结果可以直接分解N,从而恢复私钥。这个攻击(Bellcore攻击)在1997年提出至今,依然是RSA-CRT实现里最致命的威胁。

防御RSA-CRT的标准做法是计算完成后验签:用公钥验证签名结果是否合法,不合法就拒绝输出。验签的代价比签名小得多,而且公钥是公开信息,不存在密钥泄露风险。我的建议是验签逻辑不可省略,而且验签用的公钥必须来自安全的非易失存储区域,不能存在可被故障注入篡改的动态内存里——否则攻击者把公钥也改了,验签就成了摆设。

ECC方面,故障注入的主要目标是让标量乘法([k]P)产生错误的中间点,尤其是让计算中某些坐标值变为无穷远点(零点),攻击者可通过错误点反推标量k。防御手段是在标量乘法过程中对每个中间点做曲线方程校验:验证点是否还在曲线上,如果在曲线外立即中止。具体实现时,坐标的x、y、z三个值都要单独校验,不能只验x。另外,还可以在计算结束后做一次“随机点伴随”验证——在计算前生成一个随机数r,计算出[r]P,在计算完成后用[r]P校验结果的一致性。这类方法需要消耗一定的额外计算时间,但对抗标量乘法故障注入非常有效。

6.4 密钥存储与使用时的故障注入边界防护

密钥的生命周期管理往往是故障注入防护里最薄弱的环节。许多产品的密钥由外部安全芯片管理,但如果主控在向安全芯片发送密钥使用指令时被注入了故障,可能把“使用密钥A签名”篡改成“使用密钥B导出”或者“擦除全部密钥”。

我的建议是:主控与安全芯片之间的通信必须使用带消息认证码(MAC)的协议,确保指令在执行前经过完整性和来源校验;每次使用密钥的请求必须包含随机数nonce和递增计数器,安全芯片只有在nonce有效且计数器单调递增时才执行,这样即便攻击者重放指令,也无法重复使用旧请求。

密钥本身的使用过程也要有故障容忍设计。比如密钥加载到寄存器后,先校验整个寄存器组的数据完整性(CRC/奇偶校验),再执行密码运算。运算结束后立即清除寄存器中的密钥材料,避免密钥残留时间过长而扩大故障注入的攻击窗口。清除动作也建议执行两次,并在两次清除之间插入随机延迟——攻击者可能瞄准“判断清除是否完成的分支”来阻止清除动作。

7. 检测、响应与故障恢复机制

7.1 异常事件检测:从单一告警到关联分析

故障注入攻击留下的痕迹不会像软件入侵那样有明确的日志记录,但物理层、时序层、数据完整性层往往会出现多个“小异常”。我在设计检测体系时,会把各类异常事件汇集到一个统一的安全事件处理器,再做关联分析。

一个典型的攻击过程中可能出现的异常序列是:电磁毛刺引起供电短暂波动(电压监测器触发)→ 某个关键变量校验失败(软件冗余检查触发)→ 密码协处理器报告了一个罕见错误(算法引擎状态异常)。单独看每一类事件,都可能被当成偶发干扰忽略;但放在一起看,它们的关联性就是一次明确的攻击信号。

设计检测系统时,我会定义三档安全响应等级。第一档是“观察级”,单个低风险异常只记录日志,不中断系统运行;第二档是“警示级”,两个及以上低风险异常在短时间内相继出现时,主动触发一次完整的安全状态自检;第三档是“应急级”,检测到高置信度的攻击特征(比如电压监测和CRC校验同时失败)时,立即执行擦除密钥、锁定设备、进入安全故障状态等动作。不同等级响应之间应该配置独立的阈值和超时机制,避免攻击者通过频繁触发低等级事件让系统疲于应对而错过真正的攻击。

7.2 故障响应策略:擦除、锁定、还是静默降级

面对一次疑似故障注入攻击,系统应该做出什么响应?这个决策非常关键。我在这部分反复跟产品经理、运维团队争论过,最后形成了三个原则。

第一原则是“安全关键信息必须立即失效”。一旦检测到攻击,先将所有活跃的关键密钥材料清零、将安全状态机强制置为“未认证”状态。这样即便攻击者后续成功突破了其他检查,它也无法在这个状态下获得任何敏感访问权限。

第二原则是“尽量做到非破坏性”。如果一个安全事件只是因为环境干扰导致的偶发抖动,直接把设备变砖会造成巨大的维护成本。所以高置信度攻击事件触发的动作是“锁定”而不是“擦除”——锁定后设备仍然能启动到受限恢复模式,但所有安全功能关闭,必须通过授权流程解锁。只有检测到等级极高且反复出现(比如连续3次)的攻击信号时,才执行彻底的密钥自毁。

第三原则是“有攻击痕迹的失败要刻意失败”。防止攻击者把自己注入的故障伪装成普通的随机错误。检测到异常时,返回的错误码要特意设计成与普通随机错误不同的值,同时要在日志中写入带时间戳和事件ID的记录,甚至通过带外通道(比如加密的运维上报通道)把异常事件通知到后台。那些试图用概率来掩盖攻击痕迹的攻击者,最忌讳的就是系统明确知道自己被打过。

7.3 故障恢复流程:从安全状态回到正常运行

设定恢复流程时,我踩过的坑是“恢复流程本身成为攻击面”。曾经有一个设备,检测到故障注入后重启,重启后一切正常,看起来安全无虞。后来做红队测试时发现,攻击者可以在“故障处理例程准备好但还没重启”的窗口内注入第二个毛刺,阻止重启动作,同时跳过之前的错误处理逻辑,让系统带着攻击者的目标状态继续运行。

最终的解决办法是:故障响应和恢复动作尽量在硬件层面完成,而不是依赖软件例程。比如由外部看门狗负责强制断电重启,软件侧只负责设置安全状态标志和保存日志,不负责“决定何时恢复”。恢复之后,系统必须先运行一个“健康检查例程”,确认所有传感器的自检结果、关键数据区的完整性、安全状态机所处位置都符合预期,才能重新进入正常工作流程。健康检查如果发现还有任何残留异常,就直接进入更严格的故障处理等级,不能自行无限循环重试。

8. 验证与评估:怎么证明防御真的有效

8.1 自建故障注入测试平台的基本构成

防御方案做完了,怎么验证有效性?最靠谱的方法是自己先当一回攻击者。我建议安全研发团队尽早搭建一套基础的故障注入测试平台,不需要一开始就采购几十万的激光台,有几个关键组件就可以做初步验证。

平台通常由四部分组成:待测设备(DUT)注入装置触发控制结果观测。注入装置最入门的是“电压毛刺发生器”,用一个大功率MOSFET、电容和精密延时电路就能搭出来,成本几百到几千元不等。如果预算允许,再上一台电磁注入探头和配套脉冲源,就能覆盖大多数攻击手法。触发控制部分需要和DUT的某个活动信号(比如串口输出、GPIO翻转、电流波形)做同步,精确定位注入时刻。

结果观测则是最重要的部分。不能只观测“设备最终是否正常工作”,而要把安全关键状态(比如认证状态寄存器、密钥完好标志、安全日志条目)在每次测试前后的变化记录下来。一套好的测试平台应该能自动遍历毛刺宽度、深度、注入时刻三个参数空间,记录每组参数下的成功失败结果,最终输出一张“故障注入成功率热力图”。

8.2 怎么判断一个防御方案“通过”了

评估防御方案的时候,一个常见的误区是只看“攻击是否成功”。这里要引入两个更细致的指标:故障注入成功率和检测覆盖率。

故障注入成功率指 “在N次注入中有M次让安全关键状态跳出预期,并且没有被检测到”。检测覆盖率指“所有实际造成的异常状态中,有多少比例被防御机制发现并进入了错误处理流程”。一个设计优良的系统,哪怕无法把攻击成功率降到0,至少要把检测覆盖率拉到95%以上——因为检测到并且失败得安全,意味着攻击者获得了无用的输出;只有既影响安全状态又没被检测到,才算真正有效攻击。

在测试过程中,我会额外关注“半成功”状态:设备没有完全崩溃,但也没有按预期走向安全失败,而是进入了某个不确定状态。这类状态是最危险的,因为它可能让系统既无法正常服务,也没有触发安全报警,攻击者反而可以利用这种“中间态”继续推进攻击。我在代码审计时会专门审查所有错误处理路径,确保每个“失败”分支都会导致系统进入明确的、经过设计的安全状态。

实践中还有一个经验:防御方案测完“攻击不成功”还不够,还要测“攻击不成功时系统是否能正常恢复”。理想的情况是设备在攻击后能自动重启并进入正常状态(对于机会型攻击者最常见的场景),而不是动不动就锁死。

8.3 红队测试中的真实案例分析

分享一个我们做过的一次红队测试结果。被测设备是一台TEE安全芯片驱动的支付终端,团队在固件中实现了CFI、双重执行、关键变量冗余存储等多重软件防护。我们用EMFI探头对主控芯片做了全自动扫描,在约2万次注入中发现了3个“敏感窗口”,其中1个窗口能让设备跳过一个非关键的日志记录步骤,但没有造成安全越权;另外2个窗口会导致设备看门狗复位,属于无效故障。

不过,这并非说明系统绝对安全。红队把扫描结果和固件反汇编结果对比后发现,有一个安全关键函数虽然做了双重执行,但编译器在优化时把两次调用的指令重排到了一起,导致一个宽毛刺可能同时影响两次计算。虽然最终测试没有利用成功,但这个隐患足以让我们在后续构建选项中显式关闭这个函数的优化,并加入汇编级指令屏障保证隔离。

这个案例说明,软件层面的防御和编译器的交互非常微妙。“纸面上设计合理的防护”可能因为编译器优化、链接顺序、代码对齐等问题在实际上打了折扣。所以我强烈建议,安全关键函数在发布前要检查反汇编代码,确认加固逻辑没有在优化级别-O2/-O3下被意外简化。

9. 常见问题与排查技巧实录

9.1 为什么设备在攻击测试中经常“莫名其妙重启”

在故障注入测试中,最常见的结果不是“安全绕过成功”,而是“设备莫名重启”。许多团队会把重启当成“攻击失败”的记录,但我的建议是认真统计重启频率。重启本身就是系统对异常的一种反应——如果系统在注入测试中频繁重启,说明系统的供电监测、看门狗等机制在真正起作用,攻击者想单靠毛刺就绕过安全逻辑难度很高。

但如果重启频率过高以至于影响到正常体验,那说明电压监测的阈值设置得太敏感了。我在调试中遇到过一个案例:设的电压阈值接近正常工作电压波动范围的上限,结果设备在上电瞬间、大电流负载切换时偶发重启,巡检日志全是Brown-Out事件。排查方法是把测试设备接上示波器,记录启动和运行不同阶段的实际电压波形,观察电压最低点与阈值的余量,再留出至少100mV的迟滞带。

9.2 安全检测代码报错,但找不到攻击源

系统偶尔触发“安全事件报警”,但现场排查又找不到任何故障注入设备,这是很让人头疼的问题。常见的可能性有三类:一是电源质量差,市电接入的毛刺通过电源路径传导到芯片,让BOD误触发;二是强射频干扰,旁边的大功率无线设备或变频器产生窄带干扰;三是软件自身存在bug,例如某个校验函数的校验值计算有误,导致正常情况下的误报。

排查这类问题不能只盯安全模块本身。我会先从系统的供电环境入手,用一个高带宽示波器和差分探头长时间记录供电波形,用电网质量分析仪做几天的连续监测;同时把安全事件处理器的日志级别调高,记录触发时刻的系统上下文(正在执行哪个任务、外设状态、系统负载)。如果确认无外部攻击,就要考虑代码逻辑层面,重点检查是否有数据竞争或者中断嵌套导出的校验值被破坏。最有效的工具是做一个“攻击特征指纹库”,把不同原因导致的报警归类存放,积累一段时间后就能按概率排序快速定位。

9.3 编译器优化“吃掉”防御代码的排查方法

编译器优化导致安全防御逻辑失效是特别隐蔽的问题。之前讨论过双重执行被重排、CFI校验被合并这类案例,这里补充一个排查方法和一个规避手段。

排查方法是在编译完成后检查安全关键函数的汇编代码。关注几个点:校验变量是否被优化掉(明明做了冗余赋值,汇编里却找不到对镜像变量的写操作)、两次验证之间是否插入了编译器自动的指令重排序、安全分支是否被合并成单一比较。如果发现可疑点,直接看编译器的优化报告(-fopt-info)或者用objdump反汇编核对。

规避手段则包括:在安全关键函数上使用__attribute__((optimize("O0")))或者__attribute__((noinline));把关键变量声明为volatile防止被优化;在验证逻辑之间插入汇编级的内存屏障(barrier)或者用编译器提供的atomic/fence原语。这里要注意的是,这些手段虽然可靠,但会牺牲一定的性能和代码密度,所以只建议用在安全关键路径上,不要一股脑都用上。

9.4 安全事件处理函数本身被注入攻击怎么办

最后一个经典问题:如果攻击者的目标不是主流程,而是安全事件处理函数本身呢?检测机制再好,如果处理函数被绕过,这些机制就形同虚设。

我的实践心得是:安全事件处理逻辑必须和设备主应用隔离。在硬件上,使用MPU/TrustZone把安全处理代码放在独立的受保护区域;在软件上,安全处理函数不依赖主应用程序提供任何服务,包括内存分配、日志写入、网络上报都不依赖主应用。同时,检测到攻击后触发的第一动作应该尽量是“硬件动作”——例如拉高某个复位引脚、关闭外设电源、触发DMA擦除——不能只停在软件变量上。我曾经把“三重触发机制”写进设计文档:第一重是软件中断,第二重是外部看门狗,第三重是安全芯片的独立GPIO输出。任何一重视乎失效,其他两重都能兜底,整体攻击成功率会被压得非常低。

10. 从项目实践出发的一些个人体会

写了这么多技术细节,最后分享几段真实的操作体会。

第一点:故障注入防御不是一次性工程,而是一个持续对抗的过程。攻击设备越来越便宜、攻击手法越来越自动化的今天,固件升级、甚至现场部署的设备都可能成为新的攻击目标。防御设计必须跟着威胁演进不断迭代,不能有一劳永逸的心态。

第二点:故障注入攻击的防御,本质上是“成本和不确定性的游戏”。能把攻击者的成功率降到一个不可接受的区间,防御就算成功;追求百分之百的绝对防护,在工程上既不现实也没有必要——更重要的是设计出“即使攻击成功也不会造成不可控损失”的系统结构。

第三点:防止被攻击的一个隐含前提是“开发团队自己先理解攻击”。我见过太多防御方案是照着安全清单逐条打勾做出来的,没有真正理解每条防御在对抗何种攻击手段,反而在遇到组合攻击时彻底失效。安全设计和攻击理解是一体两面,只有先把自己放在攻击者的位置上思考,才能把防御做扎实。

最后,如果你正准备为自己的产品加入故障注入防护,我的建议是从最简单的“冗余存储 + 控制流校验 + 独立电压监控”起步,先用基础防线拦住绝大多数机会型攻击者,再用红队测试实际检验防线强度,然后针对暴露出的弱点逐步加固。防线可以慢慢加厚,但方向和思路一定要在一开始就摆正。这套方法在我经历的项目中反复验证过,稳定、务实,也希望对你的设计有所帮助。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 6:44:25

基于Spark的TPC-DS性能测试实战:从环境搭建到深度调优

1. 项目概述:为什么用Spark做TPC-DS性能测试?如果你负责大数据平台的选型、调优或者容量规划,那你肯定绕不开一个灵魂拷问:我们这套系统,到底性能怎么样?能扛住多大的数据量和多复杂的查询?这时…

作者头像 李华
网站建设 2026/8/26 6:43:12

OpenClaw开源AI智能体框架:从核心架构到实战部署与技能开发

1. 项目概述:为什么OpenClaw能成为“龙虾”?最近在AI智能体这个圈子里,OpenClaw这个名字可以说是火得一塌糊涂,大家亲切地叫它“龙虾”。如果你还没听说过,那可能有点落伍了。简单来说,OpenClaw是一个开源的…

作者头像 李华
网站建设 2026/8/26 6:41:22

2026MathorCup妈妈杯A题全套资源深度拆解:论文+代码+思路一次讲透

简介:数学建模竞赛是考察团队将实际问题抽象为数学模型并用编程求解的综合性赛事,其核心在于建立可靠的模型框架、高效的数值算法与清晰的论文表达。掌握一套完整的解题流程,能够显著提升备赛效率与获奖概率。在MathorCup(妈妈杯&…

作者头像 李华
网站建设 2026/8/26 6:41:10

C#部署Detic:用ONNX Runtime实现21k类开放式词汇检测

简介:物体检测是计算机视觉的核心任务之一,传统YOLO等模型受限于固定类别数,难以应对数万类别的工业质检场景。开放式词汇检测通过CLIP文本-视觉对齐,将类别名称编码为文本向量,使模型可识别训练中未见的类别。Detic借…

作者头像 李华
网站建设 2026/8/26 6:38:35

REANA:国产汽车功能安全、网络安全与SOTIF一体化协同平台解析

1. 项目概述:为什么我们需要一个“三合一”的汽车安全平台?干了十几年汽车电子,从早期的ECU刷写到现在的域控制器开发,我亲眼看着汽车从一个“机械简单电子”的产品,演变成了一个跑在轮子上的复杂计算中心。随之而来的…

作者头像 李华
网站建设 2026/8/26 6:37:00

秒杀自动化脚本的工程解析:从接口模拟到高并发优化

简介:在电商高并发场景下,自动化脚本常被用于秒杀抢购,但其背后涉及的远不止模拟点击。通过理解HTTP请求与响应模型,开发者可以从UI自动化转向接口请求模拟,大幅减少网络往返次数,提升请求命中率。时间同步…

作者头像 李华