1. 状态机基础与安全威胁模型
大家好,我是你们的硬件安全讲师。今天咱们聊聊状态机——这个在数字电路里随处可见,却又经常被忽视安全问题的老朋友。
说实话,我做了十几年硬件安全,见过太多因为状态机设计不当导致的严重漏洞。有的能让攻击者直接跳过认证,有的能让系统死锁在某个危险状态。嗯,这些坑我都踩过,今天分享给你们,希望能帮大家少走弯路。
1.1 状态机基本概念
状态机,说白了就是描述系统在不同状态下如何响应输入、如何切换状态的数学模型。你想想看,我们日常用的手机、汽车ECU、甚至智能门锁,底层都是状态机在驱动。
状态机主要分两种:Moore机和Mealy机。我习惯用一句话区分它们——Moore机的输出只看当前状态,Mealy机的输出还看输入。
Moore型状态机
Moore机的特点是:输出只与当前状态有关。举个例子,一个简单的门锁状态机:
// Moore型门锁状态机伪代码
enum State { LOCKED, UNLOCKED, ALARM };
State currentState = LOCKED;
State nextState(Input input) {
switch(currentState) {
case LOCKED:
if (input == CORRECT_PIN) return UNLOCKED;
if (input == WRONG_PIN) return ALARM;
return LOCKED;
case UNLOCKED:
if (input == TIMEOUT) return LOCKED;
return UNLOCKED;
case ALARM:
if (input == RESET) return LOCKED;
return ALARM;
}
}
// 输出只取决于当前状态
Output getOutput() {
switch(currentState) {
case LOCKED: return DOOR_CLOSED;
case UNLOCKED: return DOOR_OPEN;
case ALARM: return SIREN_ON;
}
}
我在项目中遇到过,有人把Moore机设计成输出依赖输入,结果综合出来的电路时序全乱了。记住,Moore机的输出寄存器只连状态寄存器,别乱加组合逻辑。
Mealy型状态机
Mealy机的输出则同时依赖当前状态和输入。它通常比Moore机更节省状态数量,但输出容易受输入毛刺影响。
// Mealy型门锁状态机伪代码
State currentState = LOCKED;
Output nextStateAndOutput(Input input) {
switch(currentState) {
case LOCKED:
if (input == CORRECT_PIN) {
currentState = UNLOCKED;
return DOOR_OPEN; // 输出依赖输入
}
if (input == WRONG_PIN) {
currentState = ALARM;
return SIREN_ON;
}
return DOOR_CLOSED;
// ...
}
}
核心区别总结:Moore机输出稳定、时序好分析;Mealy机状态少、响应快。安全设计上,我强烈推荐Moore机——输出与输入解耦,天然抗毛刺攻击。
1.2 常见安全漏洞
状态机的安全漏洞,说白了就是攻击者利用设计缺陷,让系统进入非预期的状态或路径。我归纳了三种最常见的类型。
状态跳跃(State Skip)
攻击者通过注入异常输入或时序扰动,让状态机跳过关键状态。比如跳过"认证中"直接进入"已认证"。
我曾经在某个IoT芯片的OTA升级模块里发现过这个问题。攻击者通过电源毛刺,让状态机从"下载固件"直接跳到"执行固件",绕过了签名校验。修复方案是在每个状态转换路径上加硬件看门狗。
死锁(Deadlock)
状态机卡在某个状态无法退出,或者两个状态互相等待。这在多状态机交互时尤其常见。
| 死锁类型 | 产生原因 | 典型案例 |
|---|---|---|
| 单机死锁 | 状态转换条件永远不满足 | 等待中断但中断被屏蔽 |
| 多机死锁 | 状态机A等B,B等A | 两个外设互等对方释放总线 |
| 资源死锁 | 状态机持有资源A等资源B | DMA控制器通道互锁 |
非法访问(Illegal Access)
攻击者通过非正常路径访问受保护的状态或资源。比如在未授权状态下读取安全密钥寄存器。
为什么会这样?很多设计只检查"当前状态是否允许该操作",但忽略了"从哪个状态来的"。我建议在状态转换路径上也加上权限检查。
1.3 威胁建模方法论
做安全设计,不能等出了问题再补。我习惯在设计阶段就用威胁建模把问题想清楚。这里分享我常用的方法。
STRIDE模型
微软的STRIDE模型,虽然是为软件设计的,但用在硬件状态机上也很顺手:
- Spoofing(冒充):攻击者伪造合法输入,诱导状态机进入错误状态
- Tampering(篡改):篡改状态寄存器或转换条件
- Repudiation(抵赖):状态机没有日志记录,无法追溯攻击
- Information Disclosure(信息泄露):状态机输出泄露内部状态信息
- Denial of Service(拒绝服务):通过死锁或状态风暴耗尽系统资源
- Elevation of Privilege(权限提升):从低权限状态跳跃到高权限状态
我个人习惯:在画状态转换图时,就用不同颜色标注每个状态的安全等级。红色是高危状态(如密钥访问、固件执行),绿色是安全状态(如空闲、休眠)。这样一眼就能看出哪些状态转换路径需要重点保护。
攻击树分析
攻击树是另一种好用的方法。以"绕过认证"为根节点,往下分解攻击路径:
绕过认证
├── 状态跳跃攻击
│ ├── 电源毛刺注入
│ ├── 时钟毛刺注入
│ └── 电磁干扰
├── 状态寄存器篡改
│ ├── 激光故障注入
│ ├── 电压故障注入
│ └── 软件漏洞利用
└── 状态转换条件绕过
├── 输入验证绕过
├── 时序攻击
└── 侧信道分析
每个叶子节点对应一种具体的攻击手段。设计时,针对每个叶子节点都要有对应的防护措施。
安全状态机设计原则
基于上面的威胁模型,我总结了几条铁律:
- 最小权限原则:每个状态只允许访问它需要的资源。别让"空闲"状态也能读写密钥寄存器。
- 状态转换验证:每个状态转换都要做完整性检查。我习惯用CRC校验状态转换路径。
- 看门狗机制:每个状态都要有超时退出机制。防止攻击者把状态机卡在某个状态。
- 冗余状态编码:别用简单的二进制编码。用格雷码或One-Hot编码,天然抗单粒子翻转。
- 安全状态隔离:高危状态和普通状态用硬件隔离,比如不同的电源域或时钟域。
记住:安全不是加功能,而是减风险。好的状态机设计,应该让攻击者"想跳跳不了,想卡卡不住,想看看不到"。
1.4 知识体系总览
下面这张图是我画的本章知识体系,方便大家建立整体认知:
这张图把本章内容串起来了。左边是基础,中间是威胁,右边是防护。三者缺一不可。你想想看,不懂基础就谈安全,那是空中楼阁;不懂威胁就谈防护,那是闭门造车。
我建议:学完本章后,拿你手头的一个简单状态机(比如UART接收状态机),用STRIDE模型过一遍,看看能发现几个安全漏洞。实践出真知。