一、安全威胁建模:固件与应用的攻击面分析

做嵌入式安全这么多年,我见过太多产品「裸奔」上线的案例。很多团队上来就写代码,等产品快量产了才想起来做安全测试——结果漏洞一堆,改都来不及。说白了,安全设计的第一步不是写代码,而是搞清楚:你的敌人是谁?他们能从哪下手?

这一章,我们就来聊聊威胁建模。我个人习惯把它叫做「安全领域的沙盘推演」。你不需要等到代码写完再被动挨打,而是在设计阶段就把攻击者的路数摸清楚。

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) 智能家居网关数据流图(DFD) 手机App 云服务器 通信协议栈 (MQTT/CoAP) 固件主循环 (业务逻辑) 加密模块 (TLS/AES) Flash存储 (固件/配置) 信任边界(设备内部) 控制指令 远程命令 解析后数据 待加密数据 加密结果 写入配置 说明:虚线框为信任边界,内部组件默认可信;外部实体(手机App、云服务器)为不可信源。 数据流经过信任边界时,必须进行身份验证和完整性校验。

我曾经踩过的坑:画DFD时最容易犯的错误是「信任边界画得太少」。很多团队只画了设备与云端之间的边界,却忽略了设备内部不同模块之间的边界。比如,加密模块和业务逻辑模块之间如果没有隔离,攻击者一旦攻破业务逻辑,就能直接拿到密钥。

1.4 实战流程:从DFD到威胁清单

好了,理论讲完了。咱们来走一遍完整的实战流程。我一般分四步走:

  1. 画DFD:把系统架构画出来,标出所有数据流和信任边界
  2. 逐元素分析:对DFD中的每个元素(外部实体、处理过程、数据存储、数据流),用STRIDE逐一过一遍
  3. 记录威胁:把每个威胁记录下来,包括威胁描述、影响范围、风险等级
  4. 制定缓解措施:针对每个威胁,给出具体的缓解方案

举个例子。还是那个智能家居网关,我们分析「固件主循环」这个处理过程:

STRIDE类别 威胁描述 风险等级 缓解措施
Spoofing 攻击者伪造合法指令 高 指令来源认证、消息签名
Tampering 攻击者篡改内存中的指令数据 高 MPU保护、栈保护、CFI
Repudiation 设备执行了恶意指令但无日志 中 安全日志、日志防篡改
Information Disclosure 固件主循环中的敏感数据被泄露 中 内存清零、数据最小化
Denial of Service 攻击者发送大量无效指令耗尽CPU 高 指令限流、看门狗超时
Elevation of Privilege 利用固件漏洞执行任意代码 高 安全启动、权限分离

我的习惯:做完威胁建模后,我会把结果整理成一张「威胁矩阵表」,贴在项目看板上。每次迭代都回头看一眼——新加的代码有没有引入新的威胁?旧的缓解措施有没有被绕过?安全不是一次性工作,而是持续的过程。

最后说一句。威胁建模看起来费时间,但其实是帮你省时间的。我在一个项目上,因为前期做了完整的威胁建模,发现了一个架构层面的设计缺陷——如果等代码写完再发现,至少要多花两周改架构。你想想看,是花两天做威胁建模划算,还是花两周返工划算?