今年八月的拉斯维加斯,白天四十几度,会展中心里却挤满了人。不是CES那种看新品的展会,而是安全圈的年度聚会:DEF CON和Black Hat。我在Hardware Hacking Village待了三天,桌上摆满了热风枪、示波器、逻辑分析仪,还有拆得只剩PCB的路由器、摄像头、充电桩。旁边一群人围着一位工程师,看他从SPI Flash上吹下芯片,用编程器读固件,再用binwalk把文件系统导出来,整套操作不到二十分钟。旁边屏幕上的议题列表里,“Embedded System Security”相关的议程占了满满一页。这就是今天这篇博文的背景:从拉斯维加斯现场带回来的,不只是几张名片,还有大量关于嵌入式系统安全的思考。
1. 拉斯维加斯现场:嵌入式安全为什么从“加分项”变成“及格线”
1.1 现场观察:硬件拆解和固件破解不再是极客游戏
在Hardware Hacking Village里,最让我惊讶的不是那些炫酷的破解工具,而是参与者的构成。以前这种桌子前坐着的多是满脸兴奋的极客和研究生,今年却多了不少穿polo衫、背着工牌的产品经理和项目经理。他们拿着自家公司的路由器、儿童手表、智能门锁过来,请研究团队帮忙看看启动固件里是不是硬编码了密钥,或者OTA升级是不是真的做了签名校验。
这种变化背后是实打实的压力。消费类物联网设备出货量持续增长,但很多设备的上游SDK和BSP本身就带着老旧的BusyBox、过时的OpenSSL版本,甚至默认打开调试接口。我随手翻了会场桌上几块常见开发板,其中两块通过UART直接就进了root shell,连密码都不需要。当设备以千万级规模铺出去之后,每一个这类小问题,都会变成自动化攻击工具里的一个稳定入口。
另一个信号来自漏洞赏金计划。今年好几家车机、安防摄像头和智能家居厂商在会场上公开了自己的漏洞报告奖励范围,其中“安全启动绕过”“固件解密提取”“未经签名的OTA包”都被列为核心项目。这说明厂商自己对风险点已经有了明确认知:真正值钱的漏洞,通常不是某个Web接口的SQL注入,而是整个信任链上的一环崩溃。
1.2 三个信号:SBOM、安全启动和威胁建模正在走向标配
在Black Hat的嵌入式专题里,我听到最多的三个词是:SBOM、Secure Boot、威胁建模。这是今年和往年最大的不同。过去大家聊嵌入式安全,主要集中在“芯片加密等级”“防抄板”,今年几乎所有议题都在谈“软件物料清单”“启动信任链”“设计阶段的风险评估”。
SBOM的意义不难理解:设备不再是一个黑盒子,每一层依赖、每一个第三方库都要写在清单里。Spring Security那类服务端框架漏洞为什么大家盯得紧?因为用的人太多、影响面太广。嵌入式里的libupnp、轻量级TLS库、WiFi协议栈也一样,一旦爆出通用漏洞,从路由器到摄像头全得挨个排查。没有SBOM,出事的时候连自己用了哪些组件都不知道,只能拆机看丝印。
安全启动的议题占比也在明显上升。无论是ARM生态里常见的U-Boot verified boot,还是x86设备上的UEFI Secure Boot,核心逻辑都是同一件事:从芯片内部的信任根开始,逐级验证,任何一级签名不合法就无法启动。这个机制在服务器上已经很成熟,但搬到嵌入式设备上还有大量细节要处理,比如密钥怎么存、回滚怎么防、证书怎么轮换。
至于威胁建模,最让我有共鸣的是一个说法:“如果你在设计硬件之前没有画过数据流图,那你就是把安全问题留给了售后。”会场上好几个案例都是从一张简单的数据流图开始,一步步推导出“调试接口应该熔断”“OTA包必须双重签名”这类结论。这套流程做完整,后面对应的风险清单会非常清晰。
2. 核心攻击面拆解:攻击者的手会伸向哪里
2.1 物理接口:UART、JTAG、SPI 不是“调试口”而是“安全门”
很多人觉得嵌入式系统被攻击的前提是先“连上网络”,但真正做安全评估的人都知道,物理接口往往是更优先的目标,尤其是在攻击者已经拿到设备本体的时候。
最常见的入口是UART调试串口。一块板卡上只要露出三四个引脚,上面标注着TX、RX、GND,基本就可以直接判断是调试串口。接上USB转串口模块,波特率扫一遍,如果固件没有做登录限制,直接就是一个root shell。我在自己测试过的多种开发板和量产设备里,大概有三成左右存在类似问题:调试串口完全开放,或者登录口令硬编码在源码里。
JTAG/SWD接口更直接。通过这类调试接口,攻击者可以读取CPU内部的寄存器、内存,甚至整个固件镜像。有些芯片支持通过熔丝或eFuse关闭调试口,但不少产品为了后续返修方便,出厂时根本没有执行这一步。SPI Flash则是最容易被忽略的存储介质,用编程器夹子夹住Flash芯片,或者直接把它吹下来放到编程器上,几分钟就能把整个固件导出来。
下面是一张我在评估中常用的攻击面速查表:
| 接口/介质 | 主要风险 | 常见工具 | 防护手段 |
|---|---|---|---|
| UART调试口 | 直接获取shell、查看日志与敏感信息 | USB转串口、screen、minicom | 登录认证、关闭调试口、熔丝 |
| JTAG/SWD | 读取内存、寄存器、固件,甚至修改执行流 | OpenOCD、J-Link | 关闭调试口、熔丝/OTP锁定 |
| SPI Flash | 直接导出固件、篡改启动镜像 | flashrom、编程器 | 固件加密存储、安全启动 |
| USB接口 | BadUSB、HID攻击、设备策略绕过 | USBGuard、Flipper Zero | USB设备白名单、端口管控 |
| 外部存储 | 提取配置、密钥、文件系统 | 读卡器、binwalk | 文件加密、密钥隔离 |
这块的防护思路其实就一句话:能物理关闭的接口,不要留到量产之后。很多工程师担心关闭调试口影响售后返修,但完全可以用“安全启动+签名日志”来替代裸奔的物理调试口。
2.2 固件与OTA:最容易出现的远端入口
如果说物理接口是“近身攻击”,那固件本身和OTA升级通道就是“远程打击”的主入口。这也是我工作中花时间最多的地方。
先看固件本身。很多设备出货后,固件文件很容易从官网下载、从手机App抓包、或者直接从Flash导出来。拿到固件之后,解包、逆向、寻找硬编码密钥和口令,是标准三连操作。更麻烦的是,如果固件里包含了私钥——这在真实设备里并不罕见——攻击者就可以给自己构造的恶意固件签上合法签名,从“破解”变成“合法更新”。
再看OTA升级。理想情况下,一个安全的产品应当做到三件事:传输通道加密、升级包签名验证、版本号防回滚。我在实际评估中看到最多的问题是:传输用了明文HTTP、升级包没有任何签名、或者签名校验只在App端做、设备端拿到包就直接写入。这种情况下,中间人攻击或者本地搭一个假服务器,就能把设备刷成攻击者指定的版本。
OTA环节还有一个容易被忽略的点:回滚保护。哪怕升级包签名做得很好,如果攻击者拿到了旧版本固件,而旧版本里恰好有已知漏洞,那攻击者只需把设备“降级”到那个旧版本,就能利用漏洞。所以必须在设备端固化一个版本号或者安全计数器的机制,拒绝任何低于当前版本的升级请求。
关于回滚保护的具体做法,我习惯用“最小版本号+安全计数器”的组合方式:安全启动链中记录一个单调递增的计数器,镜像里的版本号低于这个值就启动失败。这样即使攻击者拿到旧镜像,也无法绕过启动校验。这个机制在汽车电子和工控领域已经很常见,消费电子里也应该逐步跟上。
2.3 协议栈与供应链:最难防的两条暗线
网络协议栈是嵌入式设备暴露面最大的部分,却也是很多团队最不熟悉的部分。蓝牙、WiFi、Zigbee、Thread、MQTT、CoAP……每一种协议都有各自的攻击面。蓝牙协议栈的解析漏洞、WiFi驱动的缓冲区溢出、MQTT的弱认证,随便哪一个被利用,都能让设备变成僵尸网络里的一环。
相比写应用层业务代码,协议栈代码通常来自供应商SDK或者开源项目,很多设备厂商并不真正理解这些代码的内部实现。但一旦这个协议栈爆出漏洞,影响会是全局性的。一个很典型的场景:会议室里某厂商的路由器产品,被爆出WiFi驱动在解析特定长度管理帧时存在溢出,结果同一系列的上万台设备全部需要紧急更新固件。如果团队手里没有良好的固件更新机制,这种事件处理周期会非常痛苦。
供应链风险同样值得关注。现在的嵌入式产品,芯片、BSP、WiFi模组、系统库、应用框架,几乎每一个环节都来自不同的供应商。攻击者不需要直接攻击你的产品,只要在供应链的某一个环节做手脚——比如往SDK里塞一段搜集信息的代码,或者给某个开源组件提交一个包含后门的PR——就能顺着分发链路影响到大量设备。
我个人的建议是:在新项目立项时,就要建立一张“供应商安全能力表”,明确每一层BSP和模组的更新渠道、漏洞响应联系人、以及备选方案。同时,对所有引入的开源组件做依赖扫描,并把结果纳入SBOM管理。这一条在会场上也被反复提到,可见它已经不只是大厂的内部流程,而是整个行业的基本功。
3. 安全启动与可信根:嵌入式安全的地基
3.1 信任链到底在链什么
如果说嵌入式安全是一栋楼,那安全启动就是地基。没有可信的启动链,上面做再多的数据加密、应用加固,都像是建在沙子上。
安全启动的核心理念是“信任根”。SoC内部有一小块ROM代码,出厂时被固化在芯片里,不可篡改。系统上电时,CPU先执行这段ROM代码,它再去校验下一级Bootloader(比如U-Boot)的签名;Bootloader校验通过后,再去校验内核镜像的签名;内核启动后,再去挂载并验证根文件系统的完整性。每一级都验证下一级,就形成了一条完整的信任链。
这里可以类比成一个银行的授权体系:分行行长给你开一张介绍信,上面有他的签名和印章;你拿着介绍信去见风控经理,风控经理验证完印章才给你做进一步授权;每一环都在验证上一环的可信性,最终才能进入金库。如果中间任何一环的签名对不上,整个流程立刻终止。
在x86设备上,这套机制通常叫UEFI Secure Boot,密钥体系分为平台密钥PK、密钥交换密钥KEK、授权签名数据库db和禁用签名数据库dbx。在ARM、RISC-V嵌入式设备上,同样逻辑会落到U-Boot的FIT签名、OP-TEE的可信应用、或者厂商自己的BootROM方案里。虽然密钥名字不同,但链条结构大体一致。
信任根的另一个关键点在于密钥隔离。签名用的私钥必须存放在离线环境或者硬件安全模块(HSM)里,绝不能出现在构建服务器上。很多设备之所以被攻破,不是因为安全启动方案本身不行,而是因为签名私钥被开发人员放在了自己的笔记本电脑上,甚至直接提交到了Git仓库里。
3.2 实操:用 U-Boot FIT 签名给嵌入式设备加一道锁
接下来我以U-Boot的verified boot为例,讲一个可以在开发板上直接落地的安全启动配置流程。这套流程在各类ARM开发板、路由器主控、工控主板上都很常见,核心思路是让U-Boot只加载经过签名认证的FIT镜像。
FIT是U-Boot支持的一种镜像打包格式,可以把内核、设备树、ramdisk打包成一个.itb文件,并给每个子镜像附加签名。配置启动签名,需要做三件事:生成密钥对、修改U-Boot配置、使用mkimage签名。
先生成开发用的密钥对:
# 生成RSA私钥 openssl genrsa -F4 2048 > dev.key # 生成对应的公钥证书 openssl req -new -x509 -key dev.key -out dev.crt \ -subj "/CN=Embedded Image Signing Key/"然后修改U-Boot的配置,打开FIT签名验证相关选项。通常是在板卡的defconfig里加上:
CONFIG_FIT_SIGNATURE=y CONFIG_RSA=y CONFIG_OF_CONTROL=y CONFIG_OF_LIBFDT=y这里的关键是CONFIG_FIT_SIGNATURE,它让U-Boot在加载FIT镜像时强制检查签名。编译时,把公钥dev.crt编入U-Boot设备树中,这样U-Boot就能在启动阶段用公钥去验证镜像。
签名镜像时,推荐使用单独的keys目录,目录里同时存放.key和.crt文件。在构建服务器上执行:
mkdir -p keys cp dev.key keys/ cp dev.crt keys/ # 生成原始FIT镜像 mkimage -f fit.its fit.itb # 签名 mkimage -r -k keys/ -d fit.itb fit-signed.itb生成fit-signed.itb后,把它烧写到存储介质上。之后每次启动,U-Boot都会用编译进自身的公钥去验签,签名对不上就直接拒绝加载。这样哪怕攻击者拿到设备、拆了Flash、把恶意固件写进去,启动时也会被拦在最前面。
如果用的是支持UEFI的嵌入式板卡,流程类似,只是密钥名称变成了PK、KEK、db。我手边有一台华硕主板,Secure Boot选项藏在Boot菜单下,名字叫“Secure Boot Control”,和嵌入式板卡的配置入口差别很大,但一旦启用,逻辑是完全相通的:没有在db数据库里登记过的镜像一律不启动。
提示:如果你的板卡支持硬件防回滚,记得把安全计数器一并打开。光有签名验证但缺少版本回滚保护,就像给门上了锁却把旧钥匙留在门口脚垫下面。
3.3 回滚保护与A/B分区:签名之外的必修课
签名验证解决的是“镜像是否可信”的问题,回滚保护解决的是“镜像是否足够新”的问题。两者缺一不可。
常见的回滚保护做法有两种:版本号比较和安全计数器。版本号比较最简单,Bootloader或系统服务检查新镜像的版本是否不低于当前版本,低于则拒绝安装。安全计数器更硬核,它在芯片的OTP或者专用存储区域维护一个单调递增的值,每更新一次固件,这个值就增加一次,任何镜像里的计数小于当前计数值都无法启动。
A/B分区则是OTA可靠性的经典方案。系统准备两个系统分区(slot A和slot B),当前运行在A分区,OTA把新版本写入B分区,写入完成后切换启动标志位,下次从B分区启动。如果B分区启动失败,Bootloader可以自动回滚到A分区。把A/B和签名、回滚保护组合起来,就是一套从“下载”到“启动”全链路闭环的安全更新机制。
在会场上,有工程师分享过他们的经验:A/B分区的代价是多占一份存储空间,但换来的好处是,就算升级包在写入过程中断电、损坏,设备也不会变砖。对于插座、灯具这类没有屏幕、用户不可能自己刷机的设备,这是非常值得的投入。很多产品卖出去之后,唯一一次“系统维护”就是通过OTA,如果OTA不安全或者不抗中断,那售后电话会被打爆。
4. 固件安全评估实操:从“拆”到“验”的完整流程
4.1 环境准备:你只需要这些工具
做固件安全评估,不需要特别昂贵的设备。我第一次在实验室做完整评估时,用到的硬件成本加起来还不到几百块钱。下面是我的常用清单:
硬件部分:一台Linux开发机(Ubuntu即可)、USB转TTL模块(CH340/CP2102都行)、SPI Flash编程器(CH341A用的最多)、几个杜邦线、万用表、热风枪(拆芯片用,评估阶段可不拆直接用测试夹)。如果目标是带SWD/JTAG的开发板,再备一个便宜的J-Link或者ST-Link。
软件部分:binwalk、firmware-mod-kit、fwupd、Ghidra、radare2、qemu、file、hexdump、strings。装binwalk时尽量不要直接用系统包管理器,因为版本通常太老。推荐用pip安装:
pip3 install binwalk sudo apt install unzip firmware-mod-kit如果你经常做固件分析,还可以装Capstone和binwalk的依赖库,这样解包某些私有文件系统格式时成功率会高很多。我在评估中常用的顺序是:先file看整体格式,再binwalk扫结构,然后针对感兴趣的区域单独dd出来分析,最后用Ghidra对重点函数做逆向。
4.2 固件提取与解包:从 Flash 到根文件系统
固件提取的路径取决于你能拿到什么。最省事的情况是厂商官网直接提供固件下载链接,那直接下载即可。如果手里有实体设备,我会优先尝试通过串口或者SSH/SCP备份固件分区;如果系统不开放,再用SPI Flash方案。
以SPI Flash读取为例,用CH341A加测试夹,先识别Flash芯片型号,再用flashrom读取:
# 读取整个Flash镜像 sudo flashrom -p ch341a_spi -r backup.bin # 查看固件里的结构 binwalk backup.bin # 自动递归解包 binwalk -Me backup.binbinwalk扫出来的结果,常见的是U-Boot镜像、内核、CramFS或者SquashFS文件系统。如果binwalk能直接识别出文件系统,它会自动生成一个squashfs-root目录,里面就是完整的根文件系统。
接下来我喜欢用firmwalker快速筛一遍敏感信息:硬编码密码、私钥、IP地址、公钥、数据库连接字符串。这个脚本的思路很简单,就是一堆grep规则,但能帮你快速判断固件“干不干净”。
./firmwalker.sh /path/to/squashfs-root/遇到格式不常见、binwalk处理不了的情况,可以试试先用strings和hexdump手动筛数据,或者用dd把文件系统偏移部分单独抠出来。一次评估里我遇到过厂商自己魔改的私有文件系统,最后是靠识别压缩头和Magic Number,手工修复了文件系统头才解出来的。这类问题碰多了,你就知道为什么社区里一直强调“先看Magic Number,再谈自动化”。
4.3 验证能力:签名、证书和“默认口令”的实战检查
拿到固件并解包之后,下一步是回答几个关键问题:
第一,这个设备的启动链有没有签名校验?如果U-Boot的defconfig里没有打开CONFIG_FIT_SIGNATURE,或者根本没有对应的密钥节点,那说明设备对启动镜像完全没有完整性保护,任何人都可以把修改后的固件直接写回Flash。
第二,证书和密钥存在哪里?我在一些固件里看到过PEM格式的私钥明文躺在/etc目录下,甚至有的是BEGIN PRIVATE KEY块直接写在脚本里。这个问题比没有安全启动更严重——说明厂商不是没做安全设计,而是做了一半就忘了收尾。
第三,默认口令和调试口是否保留?检查/etc/passwd、shadow文件、启动脚本里有没有固定的默认账号;检查/etc/inittab或者systemd服务里有没有意外的串口getty服务。如果发现默认口令,那无论安全启动做得多好,攻击者只要通过网络登录一次就能拿到权限。
我的习惯是把这些检查结果整理成一张能力对照表,逐条打勾打叉。比如:固件是否有签名?U-Boot是否验证FIT签名?根文件系统是否只读挂载?调试口是否关闭?OTA是否验签?是否做回滚保护?存储中是否有明文密钥?这张表也是后续给厂商提修复建议的基础。
5. 常见问题与排查:安全策略落地时的典型坑
5.1 安全启动验证失败的四种原因
自己在板子上启用Secure Boot或者U-Boot verified boot的时候,最容易遇到的就是启动验证失败。这里的坑我基本都踩过一遍,归纳下来最常见的四种原因:
| 现象 | 可能原因 | 排查与对策 |
|---|---|---|
| 启动时报“Bad Signature” | 公钥没有编入Bootloader,或者签名时用的密钥和验证用的密钥不一致 | 检查U-Boot设备树里的公钥节点,确认和签名时用的dev.crt是同一对 |
| 能验证签名但启动后内核panic | 内核和设备树不匹配,或者ramdisk与内核版本不兼容 | 先不启用签名验证,切换到普通启动方式确认FIT镜像本身能跑 |
| 开启Secure Boot后进不了系统 | 镜像没有在db数据库中登记,或引导加载器版本过旧 | 在UEFI界面临时进入设置模式,导入平台密钥和授权密钥 |
| 固件更新后变砖 | 版本回滚被拦截,或A/B分区标志位异常 | 检查安全计数器是否递增、Bootloader是否支持自动回滚机制 |
遇到这类问题,我的第一建议是:先关闭签名验证跑一遍基础启动,确认镜像没问题,再开启验证。千万不要一上来就同时开Secure Boot和A/B切换,同时排查多个变量会把你绕晕。
另外,如果你在PC主板上操作Secure Boot,不同厂商的BIOS入口差异很大。我用过的华硕主板,菜单路径是Boot -> Secure Boot Control;有的主板则在Security菜单下,还有部分设备默认开启“OS Optimized Defaults”,连Windows都进不去。建议在动手之前先把这个选项截图留底,方便回退。
5.2 权限、设备策略和证书类问题怎么查
在嵌入式Linux上部署安全策略时,我经常遇到两类和PC上非常相似的问题:一类是文件权限/安全上下文错误,另一类是外部设备被安全策略拦截。
“Could not set file security for file”这类错误,在嵌入式Linux里的常见根源是:根文件系统是只读挂载,或者SELinux上下文不对。我在某次给设备安装密钥包时就碰到过一模一样的情况。排查方式很简单:
# 查看文件当前的安全上下文 ls -lZ /etc/keys/ # 恢复正确的SELinux上下文 restorecon -v /etc/keys/ # 如果根文件系统是只读的,先重新挂载 mount -o remount,rw /如果确认不是SELinux也不是只读文件系统,再看ACL和文件属主,用getfacl和ls -l核对一下。
USB设备被安全策略拦截,对应的场景是产品启用了USB设备白名单或类似USBGuard的机制。如果设备合规但被误拦截,先看策略规则:
# 查看当前USB设备状态 usbguard list-devices # 查看拦截日志 journalctl -u usbguard --since today # 如果确认属于可信设备,可以临时授权 usbguard allow-device <id>这类问题在PC上表现为“USB device has been blocked by the current security policy”,在嵌入式设备里就是产品经理拿U盘去导入配置,结果U盘直接被系统拒绝。产品上线前一定要把这类策略规则做成可维护的,别把设备管理员的日常操作堵死。
证书类问题还有一种常见现象:WiFi或者TLS连接时报“Wrong security type”。很多时候不是证书本身坏了,而是设备端只支持旧版TLS,而服务端已经升级到新版协议;或者WiFi加密方式填成了WPA2,但热点实际是WPA3。这类问题在固件迭代频繁的产品上尤其常见——新固件更新了安全策略,但配置文件还停留在上一代格式。
5.3 我踩过的三个坑
第一个坑是串口乱码。拿到一块板子,接好UART,打开screen,屏幕上全是乱码。排查了半天,最后发现两件事:一是波特率设错了,默认115200但实际是57600;二是板子的串口电平是3.3V,而我用的USB转串口模块输出也是3.3V,但杜邦线接触不良导致信号不稳。后来我养成了习惯:先查原理图,再测电平,最后才是接串口,顺序不能反。
第二个坑是签名工具版本不一致。用新版mkimage签出来的FIT镜像,放到旧版U-Boot上,Bootloader直接提示无法识别镜像结构。查了几天,最后发现U-Boot构建时用的libfdt版本太老,不兼容新FIT签名节点的表示方法。现在我做签名脚本时,都会把mkimage和U-Boot的版本号一并固化到构建产物里,避免后人踩同一个坑。
第三个坑是证书数据库满了。UEFI Secure Boot的db数据库有大小限制,某些固件只支持几十个条目。当签名证书轮换多次之后,db满了,新固件的证书导不进去,导致设备无法通过验证。这个问题的解决思路不是硬塞新证书,而是先梳理并删除废弃的旧证书,把证书的“有效期+设备关联”管理起来。嵌入式设备生命周期长,证书会越来越多,不是一锤子买卖。
6. 现场笔记:嵌入式安全团队现在最该干的三件事
6.1 在设计阶段就用威胁建模思考“谁会攻击我的设备”
从拉斯维加斯回来后,我脑子里一直盘旋着一个观点:嵌入式安全最贵的问题,都是在产品定义阶段埋下的。比如为了成本选了一颗不支持安全启动的低端MCU,比如给所有板卡统一留了UART测试点,比如OTA升级通道上线的时候没有做签名校验——这些问题的修复成本,在后期会是设计阶段的几十倍。
能够提前发现问题的手段,就是威胁建模。不需要一上来就上多复杂的框架,我在团队里用得最多的是简版数据流图+STRIDE。画清楚:数据从哪里来(传感器、网络、用户输入)、往哪里去(云端、存储、执行单元)、谁有物理接触(维修工、用户、攻击者)、谁有远程访问(App、云端接口)。然后沿着数据流,逐项检查是否存在伪造、篡改、抵赖、信息泄露、拒绝服务、权限提升的风险。
举例来说,一个智能门锁的数据流图画完,一定会发现:本地蓝牙接口绕过了云端认证,App与联网模块之间传输了临时开锁密码,维修模式下的调试口可以读取密钥存储区。这些问题在设计阶段发现,是改架构;到了量产版本才发现,就是召回。
所以我建议,在新项目启动时,安全团队至少要做一次威胁建模评审,并且把输出结果作为硬件选型和软件架构的输入条件之一。
6.2 把安全启动、OTA签名和回滚保护做成默认配置
会议上不少工程师都有一个共识:安全特性如果设计成“可选”,那选配率基本等于零。客户不会因为某个设备支持Secure Boot就多付钱,但一定会因为设备被黑而上新闻。所以正确的做法是:把安全启动、OTA签名、回滚保护这些都做成默认配置,而不是让客户自己去打开。
以OTA为例,默认配置应该是这样的:升级包使用公钥签名,设备端验签通过后才允许写入;写入到非活跃分区,完成后切换启动标志位;固件头部携带版本号,低于当前版本的请求一律拒绝。这套机制不需要客户参与,也没有“性能损失”的讨论空间——它就是设备正常运行的前提。
我理解工程师们最担心的是默认打开安全功能会增加开发调试成本。比如每次烧写固件前都要签名,调试时签名流程出问题还会拦住正常启动。解决办法其实也简单:开发构建和发布构建用不同的密钥链。开发阶段可以用开发密钥,发布固件的时候必须用生产密钥,这样既不影响日常调试,也保证了最终的交付物处在一个可信状态。
在会场上,有人分享过他们把安全启动默认打开后的经验:初期确实多了不少出问题的报告,但绝大多数是签名流程没有理顺,本质上是一套“生产发布流程”的搭建问题,而不是安全启动本身的问题。只要能坚持一个月,流程理顺之后,反而再也没有出现过“设备被刷成砖”类的售后求助。
6.3 用 SBOM 和持续监控保住发布后的阵地
设备出厂不是安全工作的结束,恰恰是开始。我见过太多团队,把安全启动做完、固件签名做好之后就认为万事大吉,结果半年后组件漏洞爆出来,自己连用了哪些组件都不知道。SBOM就是来解决这个问题的。
我建议在每次构建产物里自动生成一份SBOM,把内核版本、BusyBox组件、第三方库、编译器版本、甚至上游SDK的commit号都记录下来。出漏洞的时候,只要拿CVE数据库做一次交叉比对,就能快速定位到受影响的设备范围和需要更新的组件。这比翻代码仓库、问离职员工、拆机看丝印靠谱得多。
持续监控这件事,如果产品规模不大,可以先从轻量方案起步。设备端把日志和运行状态上报到中心端,中心端用类似Security Onion这样的平台做流量和日志分析,发现异常行为再回查固件版本和SBOM。别小看这一步,很多僵尸网络感染事件,都发生在设备出厂后的第一周——因为用户没有改默认密码、没有关调试口、也没有更新固件。如果设备本身能主动上报异常启动、异常外联、异常配置变更,安全团队就能在事态扩大前介入。
我个人的体会是,嵌入式系统安全的难点从来不是某一个技术点,而是把“启动验证、更新签名、密钥管理、日志监控”这一条链完整地串起来。每个环节单独看都有方案,但真正需要投入功夫的,是把它们变成研发流程中天经地义的一部分。
这次从拉斯维加斯回来,我在登机牌背面写了一行字提醒自己:“先画信任链,再画原理图;先做威胁建模,再写第一行代码。”嵌入式设备会越来越多地出现在家庭、工厂、医院和车辆里,安全已经不是一个可以留到最后一版固件再补的模块。希望这篇从现场带回来的笔记,能给你带来一些可以直接落地的思路。