一、安全威胁建模:固件与应用的攻击面分析
做嵌入式安全这么多年,我见过太多产品「裸奔」上线的案例。很多团队上来就写代码,等产品快量产了才想起来做安全测试——结果漏洞一堆,改都来不及。说白了,安全设计的第一步不是写代码,而是搞清楚:你的敌人是谁?他们能从哪下手?
这一章,我们就来聊聊威胁建模。我个人习惯把它叫做「安全领域的沙盘推演」。你不需要等到代码写完再被动挨打,而是在设计阶段就把攻击者的路数摸清楚。
1.1 攻击面分析:你的设备到底暴露了什么?
攻击面,就是攻击者能接触到你的系统的所有入口。我刚开始做安全时,总觉得攻击面就是网络端口和API接口。直到有一次,一个客户的产品被破解,问题出在JTAG调试接口没锁——攻击者直接拿烧录器把固件读出来了。嗯,从那以后我再也不敢小看任何物理接口。
一个典型的嵌入式设备,攻击面通常包括以下几类:
| 攻击面类型 | 具体入口 | 风险等级 |
|---|---|---|
| 物理接口 | JTAG/SWD、UART、SPI、I2C、USB | 高 |
| 网络接口 | Wi-Fi、蓝牙、以太网、蜂窝网络 | 高 |
| 固件接口 | OTA升级、Bootloader、文件系统 | 中 |
| 应用接口 | Web API、移动端SDK、云平台接口 | 中 |
| 侧信道 | 功耗分析、电磁辐射、时序分析 | 低(但难防) |
核心原则:攻击面不是越少越好,而是每个暴露的入口都必须有对应的安全控制措施。你不可能把所有接口都砍掉——产品还要用呢。
我在做智能门锁项目时,就踩过这个坑。产品经理要求支持蓝牙开锁,我们只考虑了蓝牙通信加密,却忘了蓝牙配对过程中的密钥交换环节。结果攻击者通过重放攻击,直接绕过了认证。你想想看,一个门锁能被远程重放攻击打开,这得多吓人?
1.2 STRIDE模型实战:给威胁分类
STRIDE是微软提出的威胁分类模型,每个字母代表一类威胁。我用了快十年,确实好用。它帮你把「感觉不安全」这种模糊的担忧,变成可操作的检查清单。
| 字母 | 威胁类型 | 含义 | 固件场景举例 |
|---|---|---|---|
| S | Spoofing(欺骗) | 冒充他人身份 | 伪造固件签名、冒充合法设备 |
| T | Tampering(篡改) | 修改数据或代码 | 篡改OTA升级包、修改Flash内容 |
| R | Repudiation(抵赖) | 否认做过某操作 | 缺少安全日志、日志可被删除 |
| I | Information Disclosure(信息泄露) | 敏感数据被窃取 | 固件逆向、密钥提取、内存dump |
| D | Denial of Service(拒绝服务) | 系统不可用 | 看门狗攻击、无线干扰、资源耗尽 |
| E | Elevation of Privilege(权限提升) | 获得更高权限 | 用户态提权到内核态、绕过安全启动 |
我的实战技巧:STRIDE不是一次性做完就完事的。我建议在每个设计评审会上,对着STRIDE清单过一遍。尤其是当需求变更时——新加一个功能,往往意味着新的威胁。
举个例子。我在做车载T-Box项目时,用STRIDE分析OTA升级功能:
- Spoofing:攻击者能否伪造升级服务器?→ 需要双向证书认证
- Tampering:升级包在传输中能否被篡改?→ 需要签名校验
- Repudiation:设备升级失败后能否追溯?→ 需要安全日志
- Information Disclosure:升级包中是否包含敏感信息?→ 需要加密传输
- Denial of Service:攻击者能否反复触发升级?→ 需要频率限制
- Elevation of Privilege:升级过程中能否执行恶意代码?→ 需要安全启动链
你看,一个简单的OTA功能,用STRIDE一分析,至少能发现6个潜在风险点。如果不做这个分析,你可能只想到加密传输,其他全漏了。
1.3 数据流图绘制:把攻击路径画出来
数据流图(DFD)是威胁建模的「地图」。我个人习惯先画DFD,再做STRIDE分析。因为只有把数据怎么流、经过哪些组件、存在哪里搞清楚,你才能知道每个环节可能出什么问题。
一个嵌入式系统的DFD,通常包含以下元素:
- 外部实体:用户、云服务器、手机App
- 处理过程:固件主循环、加密模块、通信协议栈
- 数据存储:Flash、RAM、EEPROM、外部存储
- 数据流:命令、状态、固件包、日志
- 信任边界:安全世界与非安全世界的分界线
下面是我为一个智能家居网关绘制的DFD示例。这张图清晰地展示了数据从手机App到云端,再到设备固件的完整路径,以及每个环节的信任边界。
我曾经踩过的坑:画DFD时最容易犯的错误是「信任边界画得太少」。很多团队只画了设备与云端之间的边界,却忽略了设备内部不同模块之间的边界。比如,加密模块和业务逻辑模块之间如果没有隔离,攻击者一旦攻破业务逻辑,就能直接拿到密钥。
1.4 实战流程:从DFD到威胁清单
好了,理论讲完了。咱们来走一遍完整的实战流程。我一般分四步走:
- 画DFD:把系统架构画出来,标出所有数据流和信任边界
- 逐元素分析:对DFD中的每个元素(外部实体、处理过程、数据存储、数据流),用STRIDE逐一过一遍
- 记录威胁:把每个威胁记录下来,包括威胁描述、影响范围、风险等级
- 制定缓解措施:针对每个威胁,给出具体的缓解方案
举个例子。还是那个智能家居网关,我们分析「固件主循环」这个处理过程:
| STRIDE类别 | 威胁描述 | 风险等级 | 缓解措施 |
|---|---|---|---|
| Spoofing | 攻击者伪造合法指令 | 高 | 指令来源认证、消息签名 |
| Tampering | 攻击者篡改内存中的指令数据 | 高 | MPU保护、栈保护、CFI |
| Repudiation | 设备执行了恶意指令但无日志 | 中 | 安全日志、日志防篡改 |
| Information Disclosure | 固件主循环中的敏感数据被泄露 | 中 | 内存清零、数据最小化 |
| Denial of Service | 攻击者发送大量无效指令耗尽CPU | 高 | 指令限流、看门狗超时 |
| Elevation of Privilege | 利用固件漏洞执行任意代码 | 高 | 安全启动、权限分离 |
我的习惯:做完威胁建模后,我会把结果整理成一张「威胁矩阵表」,贴在项目看板上。每次迭代都回头看一眼——新加的代码有没有引入新的威胁?旧的缓解措施有没有被绕过?安全不是一次性工作,而是持续的过程。
最后说一句。威胁建模看起来费时间,但其实是帮你省时间的。我在一个项目上,因为前期做了完整的威胁建模,发现了一个架构层面的设计缺陷——如果等代码写完再发现,至少要多花两周改架构。你想想看,是花两天做威胁建模划算,还是花两周返工划算?