4、抗故障注入设计:冗余计算、错误检测码、传感器检测
故障注入攻击,说白了就是攻击者通过物理手段干扰芯片的正常运行。比如突然拉低电压、用强光照射、或者注入电磁脉冲。目的就一个:让芯片在某个关键判断时出错,从而绕过安全校验。
我早年做一款金融支付终端时,就遇到过这种攻击。攻击者用激光照射芯片表面,硬是把RSA签名校验给跳过去了。那次之后,我对抗故障注入的重视程度直接拉满。
抗故障注入,不是靠单一手段能解决的。你得从三个层面同时下手:冗余计算、错误检测码、传感器检测。这三者互为补充,缺一不可。
4.1 冗余计算:让攻击者白忙活
冗余计算的核心思想很简单:同一个关键操作,我算两遍甚至三遍,结果必须一致。如果两次结果不一样,那肯定是被攻击了,直接触发安全响应。
我个人习惯把冗余分为两种:
- 时间冗余:同一段代码,在同一个核心上跑两次,比较结果。
- 空间冗余:同一段代码,在两个不同的核心或硬件模块上同时跑,交叉校验。
时间冗余实现起来简单,但有个致命弱点——如果攻击者持续注入故障,两次都可能被干扰。所以我更推荐空间冗余,尤其是用两个不同的硬件单元来做。
关键点:冗余计算不能只是简单的复制粘贴。两个分支的代码实现方式、寄存器使用、甚至编译器优化选项都要不同。否则攻击者用一个故障模式就能同时打掉两个分支。
举个例子,我在做AES密钥校验时,会这样写:
// 分支1:标准实现
uint32_t result1 = aes_encrypt_block(key, plaintext);
// 分支2:逆序实现(字节顺序反转后再算)
uint32_t result2 = reverse_bytes(aes_encrypt_block(reverse_bytes(key), reverse_bytes(plaintext)));
if (result1 != reverse_bytes(result2)) {
// 检测到故障注入,立即擦除密钥并复位
secure_wipe_and_reset();
}
你看,两个分支的输入输出都做了变换。攻击者想同时猜对两个分支的故障效果?几乎不可能。
4.2 错误检测码:给数据加把锁
冗余计算保护的是执行过程,但数据本身也可能被篡改。这时候就需要错误检测码(EDC)上场了。
常用的错误检测码有:
| 类型 | 检测能力 | 开销 | 我常用的场景 |
|---|---|---|---|
| 奇偶校验 | 单比特错误 | 极低 | 寄存器文件、小数据块 |
| CRC-16/32 | 多比特错误、突发错误 | 中等 | Flash存储、通信数据 |
| ECC(纠错码) | 单比特纠错+双比特检测 | 较高 | SRAM、关键安全参数 |
我建议在以下位置强制使用EDC:
- 安全状态寄存器:比如“是否已通过身份验证”这个标志位,必须带CRC保护。
- 密钥存储区:密钥在Flash或SRAM中存放时,每128位数据附加16位CRC。
- 程序计数器(PC):有些攻击者会尝试篡改PC值,跳转到非预期地址。硬件上对PC做奇偶校验,能有效检测这类攻击。
小技巧:错误检测码的生成多项式,不要用公开的标准多项式。你可以自己选一个本原多项式,或者把多项式值作为密钥的一部分。这样攻击者即使知道你在用CRC,也不知道具体参数。
4.3 传感器检测:感知物理攻击
冗余和EDC都是被动防御——等攻击发生了才检测。而传感器检测是主动感知——在攻击发生前或发生时,直接捕获异常物理状态。
常用的传感器有三类:
4.3.1 电压传感器
攻击者经常通过拉低或拉高供电电压来制造故障。电压传感器会实时监控VDD和VSS之间的电压差。
我记得有一次做智能卡项目,攻击者用了一个很狡猾的手法——在正常电压上叠加一个高频小幅度纹波。普通电压比较器根本检测不到。后来我换用了带通滤波器+峰值检测器的方案,专门抓这种高频扰动。
电压传感器的阈值设置很关键:
- 欠压阈值:低于标称电压的10%~15%触发告警
- 过压阈值:高于标称电压的10%触发告警
- 纹波检测:频率在1MHz~100MHz之间的电压波动,幅度超过50mV即触发
4.3.2 频率传感器
攻击者有时会改变时钟频率——要么降频让时序松弛,要么升频让建立时间违例。频率传感器就是用来抓这个的。
实现方式通常是用一个参考时钟(比如内部RC振荡器)去监测主时钟。如果主时钟频率偏离参考时钟超过一定比例,就触发复位。
注意:参考时钟本身也可能被攻击。我建议用两个不同原理的时钟源互相监测——比如一个RC振荡器,一个环形振荡器。它们对温度、电压的敏感度不同,攻击者很难同时骗过两个。
4.3.3 温度传感器
温度攻击比较极端。比如用液氮冷却芯片,或者用热风枪局部加热。温度变化会导致晶体管开关速度改变,从而引发时序错误。
温度传感器的部署位置有讲究:
- 芯片角落:检测整体温度变化
- 核心逻辑附近:检测局部热点
- 安全模块周围:重点保护区域
阈值一般设为:
- 低温告警:-20°C(正常工业级芯片最低-40°C,但-20°C以下就值得警惕)
- 高温告警:+85°C(超过这个温度,芯片可能进入非正常状态)
- 温度变化率告警:每秒变化超过10°C,基本可以确定是人为攻击
4.4 三者协同:构建纵深防御
冗余计算、错误检测码、传感器检测,这三者不是孤立的。它们需要协同工作,形成一个完整的防御闭环。
我习惯这样设计:
- 传感器检测到异常(比如电压骤降)→ 立即触发一个不可屏蔽中断(NMI)
- NMI服务程序 → 执行关键数据的冗余校验和EDC检查
- 如果发现数据被篡改 → 立即擦除密钥、清除安全状态、复位芯片
- 如果数据完好 → 记录攻击事件日志,然后复位
为什么要先做校验再复位?因为有些攻击是瞬时的——传感器检测到异常时,数据可能还没被篡改。这时候复位太早,反而给了攻击者重试的机会。先确认数据完整性,再决定是否擦除密钥,这个顺序很重要。
总结一下我的经验:
- 冗余计算:保护执行过程,用不同实现方式做交叉校验
- 错误检测码:保护数据完整性,关键数据必须带CRC或ECC
- 传感器检测:保护物理环境,电压、频率、温度一个都不能少
三者结合,才能让攻击者无从下手。我曾经见过一个产品,只做了传感器检测,没做冗余计算。结果攻击者用电磁脉冲直接绕过传感器,照样把安全校验给跳过去了。所以,别偷懒,三个都要上。
嗯,抗故障注入设计就聊到这儿。下一章我们讲侧信道攻击的防护——那又是另一个有意思的话题了。