资讯动态

嵌入式设备安全加固实战:从Secure Boot到USB管控

发布时间:2026/8/26 21:20:11 来源:尧图企业网站定制
做嵌入式设备开发的人对“安全”这两个字的理解大多都是从翻车开始的。我在实验室里调板子的时候Secure Boot 默认关着、串口全开、root 随便进一切岁月静好。等设备真正进了现场——也就是标题里说的 Live 状态——网络安全策略、USB 管控、只读文件系统、证书信任链、启动校验这些原本“存在感很低”的东西突然全变成拦路虎。一个权限位设错设备就启动不了一条 USB 策略没配好现场技术员插个 U 盘都报 blocked。这篇文章就整理一下这些年我在嵌入式系统安全上踩过的坑和沉淀下来的做法从开机到运行从芯片信任根到应用层权限一次讲透。适合做嵌入式 Linux、物联网网关、Android 定制系统、以及工业设备准入控制的工程师参考尤其是那些即将把设备从开发板推向量产的朋友。1. 别等真机翻车才想起安全嵌入式系统的“Live”问题很多人把嵌入式安全理解成“给固件加个密”或者“开机设个密码”这其实把问题想窄了。Security 在 Live 系统里是一个贯穿全生命周期的状态不是某一个功能模块。设备从上电那一刻起就要经历 BootROM 校验、Bootloader 验签、内核加载、文件系统挂载、应用启动、外设接入、网络通信任何一个环节没有约束都可能成为被攻击的突破口。我在一个车机项目里见过这样的场景量产固件的 Secure Boot 是开着的但文件系统里一个调试后门服务没删干净结果攻击者根本不需要拆机直接通过网络把后门激活了。这个案例给我的教训很深——安全是木桶效应短板在哪风险就在哪。嵌入式设备的安全目标和 PC 或云服务器差别很大。首先是性能约束很多 MCU 或者低端 SoC 根本跑不动高强度加解密比如完整 TLS 握手在某些单核芯片上能把 CPU 占满其次是存储约束安全启动需要的密钥、证书、安全策略文件都要抢占宝贵的 flash 空间再次是网络环境大量设备部署在离线内网根本无法实时拉取安全补丁漏洞一旦被利用修复周期长得可怕最后是长生命周期一台工业设备可能要用十年以上而密钥算法和证书体系在这期间可能已经换代了。这些约束决定了你不能照着互联网安全方案往嵌入式设备上硬套。所以我在规划系统安全时习惯把目标拆成四层启动安全确保只跑我们签名的代码、系统完整性确保运行时文件和数据没被篡改、外设管控确保物理接口不被滥用、应用与数据安全确保业务层面权限和加密到位。四层之间相互独立又彼此支撑哪一层被突破了其他层还能挡住大部分攻击。比如 Secure Boot 被绕过还有 dm-verity 校验文件系统文件系统被挂载成可写还有 SELinux 限制进程权限。这种纵深防御的思路是 Live 系统安全的基石。下面几节我就按这条链路逐步展开。2. 从信任根到应用层嵌入式安全的总体布局提到“信任根”这个词很多做应用层开发的同事会觉得抽象我换个说法整个系统的安全都要建立在“第一个不可伪造的信任点”上。在 PC 上这个信任点是 UEFI 固件里的密钥在手机 SoC 上是 BootROM 里固化的公钥和 eFuse 里的密钥在工业设备上常见的是独立的安全芯片或 HSM。为什么需要一个物理信任根因为软件层面的校验总会被软件攻击比如替换引导程序、注入恶意代码只有把根密钥放在芯片内部、物理上无法轻易读取的区域攻击者才没有捷径可走。这也是为什么华硕主板 BIOS 里开了 Secure Boot 后、很多系统起不来时问题往往出在“信任根里的密钥和系统不匹配”而不是主板坏了。从信任根往外是一条完整的“链式验证”路径。以典型的高通/MTK Android 设备为例链条是BootROM - SPL/ABL - U-Boot - boot.img - system/vendor - 应用签名。每一级加载下一级之前都要用上一级内置的公钥验证下一级的签名。任何一个环节验签失败系统就拒绝启动。这个设计思路和你去机场过安检是一样的每个登机口都要查一次登机牌而不是只在机场大门查一次。但这也带来一个实际问题——密钥管理极其重要。我见过有团队把签名私钥放在一个共享网盘里结果某次内网被入侵固件签名私钥直接泄露这意味着攻击者可以伪造任意固件。所以做启动安全第一件事不是写代码而是设计密钥的生命周期谁生成、谁存储、谁使用、谁销毁。然后是运行时的系统完整性。签名解决了“启动时加载的镜像是官方的”但解决不了“运行后文件被篡改”。Android 的做法是 dm-verity它把整个系统分区做成一个默克尔树块设备读取时动态校验哈希一旦发现某个块被改动立刻返回 I/O 错误。Linux 下的做法还有 IMAIntegrity Measurement Architecture它对文件进行哈希度量并和预期值比对。这些机制听着复杂但实际效果很直接设备被 root、系统分区被挂载为可写这些尝试都会被拦截或者立即暴露。不过要注意这类机制在开发调试阶段非常碍事我一般建议开发板关闭、量产机强制开启并且用构建系统把开关固化成配置避免人为忘记。最后是应用层的权限模型。嵌入式设备上的应用五花八门有的是开机自启的守护进程有的是和硬件打交道的底层服务还有的是给用户用的业务 App。Android 里靠 SELinux 加 APK 签名权限Linux 里靠 Capabilities 加 cgroup 隔离。原则只有一条——最小权限每个进程只给它完成工作所需的最小权限集。这个说起来容易做起来难我见过很多设备把平台签名密钥直接发给第三方 App 开发商就为了让他们能调用一个系统接口结果等于把整个系统安全拱手让了出去。正确的做法是自定义受保护的权限让第三方申请由系统服务统一校验。后面我会详细讲如何配置和排查这类问题。3. 启动链路怎么做才靠谱Secure Boot 与固件校验实战启动安全这部分是最“硬核”也最不好调试的因为一旦配置错误设备直接变砖而且不像软件问题可以打日志Bootloader 阶段连串口都可能没起来。我建议所有要量产的朋友提前在开发板上把 Secure Boot 的整体流程摸透熟悉每个阶段的报错表现否则等产线上批量刷机时才发现某台机器的密钥没烧对那真的是灾难。3.1 Secure Boot 的信任链与密钥体系Secure Boot 的密钥体系通常采用 PKI 结构以 UEFI 为例有三类关键密钥平台密钥PK、密钥交换密钥KEK、签名数据库db/dbx。PK 是整个体系的根只有持有 PK 私钥才能修改 KEK 和 dbKEK 用于授权更新签名数据库的实体db 里存放的是被信任的签名或证书dbx 存放被吊销的名单。系统启动时固件会校验引导加载程序的签名是否在 db 里、是否被 dbx 吊销通过才继续执行。这里我要强调一个容易被忽略的点开启 Secure Boot 不等于万事大吉密钥的生成和管理才是重点。很多主板厂商默认只预置了微软的签名密钥你自己编译的 Linux 内核如果没有做签名是过不了校验的。我在华硕主板上调试过一台双系统设备开启 Secure Boot 后自编译内核直接拒载排查了半天发现是没把自建证书导入 db。解决方法是进 BIOS 的 Secure Boot 管理界面把自定义证书或签名的 .efi 引导文件加入白名单或者干脆在构建内核时用本地私钥签一次 boot 镜像。另外Secure Boot 开启后部分老显卡的 Option ROM 因为不带签名会被拦截表现就是开机黑屏——这时候要检查 PCIe 设备的固件是否支持而不是怀疑主板坏了。3.2 固件升级包的签名与防回滚设备出厂之后固件升级是安全链条里最容易出问题的环节。攻击者最常见的攻击手段有两种一是伪造一个包含恶意代码的“升级包”骗设备刷入二是把一个存在已知漏洞的旧版本固件“回滚”刷进设备这就是降级攻击。针对第一种需要在升级包层面做数字签名校验用升级公钥验证固件包的摘要和签名针对第二种需要在设备里维护一个单调递增的版本计数器Bootloader 每次刷完新固件就更新计数器并且拒绝任何版本号低于当前值的镜像。实际项目中我建议把“签名验证”放在两个地方一是设备侧的升级服务比如 Android 的 recovery 模式二是升级服务器侧。设备侧验证保证恶意包进不了系统服务器侧验证保证即使有人拖走了升级包文件也无法伪造新的恶意包。如果你用的是 OTA 方案最好在服务端给每个设备生成唯一的升级令牌防止合法的升级包被离线重放。此外所有升级过程都要有断电保护——写入新固件前先写一个 flag 分区标记状态bootloader 启动时检查这个 flag如果发现升级中断要自动回到上一个可用版本。这个机制我当时在设计时多花了两天后来在真机上模拟断电场景救回了至少三块开发板。3.3 开发阶段如何验证 Secure Boot 是否真的生效很多团队做到最后Secure Boot 是“开了”但没验证过这不叫安全叫心理安慰。我的习惯是在测试阶段做两个验证动作。第一个是破坏性验证把一张未签名的内核镜像或错误签名的 boot.img 刷进设备确认系统拒绝启动。第二个是完整性验证启动后用cat /sys/firmware/efi/efivars/SecureBoot-*或在高通平台查fastboot getvar secure确认 Secure Boot 状态是 enabled。针对 Android 设备还可以通过adb shell getprop ro.boot.verifiedbootstate查看 verified boot 状态正常量产的设备应该是 green如果是 orange 或 yellow 就说明验证链有问题。这个步骤一定要写进产测流程不能省。4. 文件系统与证书权限最常见的“现场翻车点”如果说 Secure Boot 是启动阶段的守门员那文件系统和证书权限就是运行阶段最容易出乱子的地方。我在项目现场见到的报错十有八九和这一层有关。有些问题是设计缺陷有些纯粹是运维踩坑但结果都一样服务起不来设备挂掉技术员抓瞎。4.1 为什么 /system 必须只读一个模拟器报错引发的思考先从一个很典型的报错说起。很多做 Android 模拟器开发的朋友应该都有过这个经历用 adb 往/system/etc/security/cacerts里推 CA 证书结果 shell 提示 remote couldnt create file: read-only file system。这个报错的直接原因就是/system分区是只读挂载的但这背后其实是好几层安全设计在起作用。从 Android 4.4 开始系统分区默认只读后续版本又加入了 dm-verity 和 SELinux三重保护叠加挂载标志是 ro、块设备有完整性校验、文件上下文受 SELinux 策略限制。哪怕你用adb root拿到了 root 权限想往/system里写文件还得先adb disable-verity、adb remount而且需要解锁 bootloader——这在量产设备上通常是不允许的。所以这个报错不是 bug是系统在保护自己。那正确做法是什么在 Android 设备上用户级 CA 证书应该安装到/data/misc/user/0/cacerts-added/或者通过系统的“安装证书”入口导入系统会为每个证书生成一个 hash 命名的文件并放到用户 CA 存储区。真机上如果确实需要替换系统 CA比如企业内网要装私有 CA正规路径是修改系统镜像并重新签名、刷机而不是在运行的时候用 adb 硬来。模拟器上我见过有人用-writable-system参数启动再adb remount这只能在开发环境用而且每次冷启动都会恢复只读状态。要时刻记住你越方便地改系统分区攻击者就越方便地改你的设备。4.2 文件安全属性设置失败的排查思路热搜词里有一条 could not set file security for file这个报错我在交叉编译环境和 Windows 共享目录上见过很多次。它的本质是你尝试设置文件的 ACL、属主或扩展安全属性比如 SELinux 上下文、POSIX capability但文件系统或当前权限不允许这么做。举几个实际场景。场景一在 Windows 共享的 FAT/exFAT 格式盘上交叉编译 Linux 内核然后试图用setcap cap_net_rawep ./app会直接报 “setting capabilities on file failed: Operation not supported”因为 FAT 不支持扩展属性。解决方法是把文件放到 ext4 格式的分区上再设置。场景二在多用户 Linux 服务器上用普通用户执行chown root:root file会报 “Operation not permitted”这是内核强制用户权限不是 bug应该用 sudo。场景三在 Windows 的 NTFS 文件夹上程序试图修改文件的安全描述符Security Descriptor但当前用户没有该文件的“更改权限”权限会弹 “Unable to set file security” 之类的提示这在共享目录里特别常见根因往往是共享权限和 NTFS 权限叠加取的是交集任何一个权限位不够都会失败。遇到这类问题先别急着查业务代码用ls -lZ看 SELinux 上下文、用getfacl看 ACL、用mount看文件系统类型和挂载参数基本能定位到 80% 的原因。Windows 上则用icacls查看有效权限确认当前用户对目标路径有“修改”和“写入”权限而不是只看共享权限是否全开。4.3 SELinux 策略不是摆设从 avc denied 到放行SELinux 是嵌入式 Linux/Android 系统里最劝退新手的组件之一也恰恰是 Live 系统安全最不能省的防线。它的核心机制是强制访问控制MAC不管进程是 root 还是普通用户所有访问都要经过策略检查。没有 SELinux 时拿 root 等于无敌有了 SELinuxroot 打不开/data里的敏感文件也很正常。我强烈建议在使用 SELinux 的系统上始终开启 enforcing 模式并且通过dmesg | grep avc或者adb logcat -b events | grep avc来观察被拒绝的访问记录。一条典型的 avc denied 日志长这样avc: denied { read write } for pid1234 commmy_service nameconfig.db devmmcblk0p25 ino2457 scontextu:r:untrusted_app:s0:c512,c768 tcontextu:object_r:vendor_data_file:s0 tclassfile这段日志的含义是untrusted_app域里的进程尝试读写vendor_data_file类型的文件被策略拦住了。要放行需要在你自己的 te 策略文件里加一条 allow 规则allow untrusted_app vendor_data_file:file { read write open };但我要提醒看到 denied 不要本能地第一时间放行先想想这个访问是不是真的合理。如果某个系统服务尝试读/data/user/0下的其他 App 私有目录那这本身就是高风险行为正确做法是改造应用架构而不是无脑加 allow。在 Live 环境里安全策略的每一分宽松都是给攻击者留的一分空间。4.4 数据库权限也多留个心眼在物联网后台或者设备端数据库上热搜词里那个create algorithmundefined definer... sql security definer ... view information_schema.views的报错也值得聊两句。它的核心不是语法问题而是权限定义问题创建 View 时用了SQL SECURITY DEFINER意味着任何用户查询这个视图时都按定义者的权限去执行。如果定义者是mysql.infoschemalocalhost这种高权限账号而视图内容又被篡改就可能出现越权读数据的情况。在嵌入式设备的本地数据库上我建议所有视图和存储过程都遵循两个原则一是用最小权限账号创建二是明确指定SQL SECURITY INVOKER即按调用者的权限执行而不是按定义者的权限执行。这跟前面讲的最小权限原则是同一个道理——权限不要悄悄放大。5. USB 与外设策略运行态设备管控的攻防细节设备一旦进入 Live 状态物理接口的管理往往比远程攻击更棘手。因为攻击者可能直接走到设备跟前插一个 U 盘、接一个键盘、或者通过 USB 线把 PC 和设备的调试口连起来。这类攻击在工业现场尤其常见所以外设管控是嵌入式系统安全里不能省的一环。5.1 USB 设备管控从策略配置到报错排查热搜词里那条 USB device has been blocked by the current security policy 是 Windows 端点管控里很典型的报错。这类策略通常由企业的 MDM移动设备管理或 DLP数据防泄漏软件统一下发通过组策略或注册表设置HKLM\SOFTWARE\Policies\Microsoft\Windows\RemovableStorageDevices下的权限项把可移动存储类设备的读写权限全部 deny。一旦策略生效用户插入 U 盘会看到设备被策略拦截的提示完全没法用。在 Android 设备上外设管控通常走 DevicePolicyManager。比如设备所有者Device Owner可以通过setUsbDataSignalingEnabled(false)禁止 USB 数据连接只允许充电也可以使用addUserRestriction里的DISALLOW_USB_FILE_TRANSFER限制文件传输。如果设备是 kiosk 模式比如自助终端、工控平板我建议在系统层面把 USB 默认切到充电模式同时用 SELinux 策略限制 vold 挂载 USB 存储的行为。这么做的好处是即使攻击者物理接触设备插上 U 盘也无法挂载读取数据。还有一条常见的现场报错 this action is not allowed with this security level configuration这个在 Android 上通常意味着你调用的 API 需要更高的角色权限。比如某个管理应用想设置 USB 规则但它的设备管理角色不是 Device Owner系统就会拒绝。排查思路很简单确认当前应用是否已经被设置为设备所有者用dpm set-device-owner命令行进行配置如果已经设置了还报错检查应用是否正确处理了onDisableReasonChanged回调或者是否因为安全补丁升级导致 API 行为变化。这事没有太多捷径就是要一板一眼地核对 Device Admin/Device Owner 的状态。5.2 安全策略配置的统一管理设备现场的 USB 拦截、外设白名单、应用安装限制这些策略如果只靠人工一台一台配迟早出大乱子。我见过一个项目因为运营同事在几十台设备上手工配错了策略版本结果部分设备能插 U 盘部分不能排查花了一整天。后来我们把策略做成了签名配置文件放在系统分区里每次开机由安全服务读取并应用同时服务器侧记录各设备的策略版本号发现版本不匹配自动告警。这个经验可以直接复用到 Windows 场景。比如安全中心的注册表项被人改了或者杀毒状态在系统里显示异常排查时先看组策略有没有被本地管理员覆盖再检查HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Security Center\Provider\Av下的 Provider 注册项是否被篡改或缺失。很多情况下不是杀毒软件本身不行而是注册表里的安全中心状态和实际服务状态对不上导致系统误报“未启用安全防护”。Linux 侧同理不要逐个文件散落配置把/etc/security下的所有策略文件纳入统一的版本管理和固件一起发布才能确保所有设备策略一致。5.3 操作系统自身的安全组件别乱动有些朋友一遇到 Windows 安全中心变英文、或者某天突然打不开安全中心就急着去改注册表或者关掉 Defender。我劝你先想清楚这真的是“软件问题”还是你触碰了安全基线Windows 安全中心界面语言跟随系统区域设置只要安装了对应语言包并把区域切到中文界面一般会跟着变。如果只改系统区域但界面仍然是英文大概率是系统镜像没有包含中文语言包而不是设置项藏在哪里。另外企业环境里安全中心的检测状态经常被第三方杀毒软件接管此时 Windows 安全中心会显示“由 XXX 管理”这是正常的多供应商协作状态不代表系统不安全。真正需要警惕的是安全中心被注册表策略或恶意软件静默禁用——如果你发现安全服务都被关停且无法重新打开那就得走完整的系统排查流程了。对于嵌入式设备尤其是 Windows IoT 设备我建议出厂时就锁定安全管理策略避免现场操作人员随意改动。6. 现场报错速查那些最磨人的错误信息把实际项目中遇到的高频报错整理成一张速查表方便大家遇到问题先对号入座。这些信息都是我从真实现场记录下来的有些看起来简单但在紧张排障时非常有用。报错信息典型场景根因分析解决思路could not set file security for file编译/部署把文件写到共享盘或 FAT 分区文件系统不支持 ACL/扩展属性或当前用户权限不足改用 ext4/NTFS用icacls或chown检查并修正权限/system/etc/security/cacerts ... read-onlyadb 往 Android 系统证书目录推证书/system 只读挂载 dm-verity SELinux用户证书导入/data/misc/user/0/cacerts-added系统证书需重新制作镜像并签名this action is not allowed with this security level configuration.Android 管理 App 调用受控 API应用不是 Device Owner 或 Device Admin或策略等级不够用dpm set-device-owner配置角色核对回调和权限声明USB device has been blocked by the current security policyWindows 系统插入 U 盘被拦截组策略或 MDM 禁用了可移动存储找到下发策略的管理端按需调整切勿本地硬改注册表绕过hkey_local_machine\software\microsoft\security center\provider\av\相关注册表缺失Windows 安全中心显示异常第三方杀软接管或注册表项损坏检查杀软安装状态修复安全中心 Provider 注册项create algorithmundefined definermysql.infoschema...数据库视图/存储过程权限报错SQL SECURITY DEFINER 与高权限定义者绑定改用最小权限账号指定 SQL SECURITY INVOKER华硕主板开启 Secure Boot 后黑屏/无法引导自编译系统或老设备开机失败引导加载程序未签名或 Option ROM 不兼容在 db 中导入自建证书/签名引导文件关闭 CMS确认显卡固件兼容avc: denied日志刷屏SELinux enforcing 模式下服务被拒绝进程域与目标文件上下文不匹配根据日志写最小化 allow 规则审慎放行这个表里的问题多数在开发环境根本不会暴露因为开发板通常把 SELinux 关在 permissive、文件系统可写、Secure Boot 不开所有权限都宽松得很。等设备走上产线、进入 Live 状态这些约束一个接一个回来问题就接踵而至。所以我的建议是越早让开发环境模拟量产的“收紧”状态越能提前发现这些问题而不是等到现场才排查。具体可以每周做一次全量构建在真机上开启所有安全项跑一遍冒烟测试把报错提前消化在实验室里。7. 几条实践心得写给打算做安全的你最后聊几句比较私人的体会。做嵌入式系统安全这几年我最大的感受是安全不全是技术问题更是管理问题。比如密钥管理技术方案再完美如果私钥在共享网盘里躺着一切归零。很多攻击者根本不需要破解芯片只需要买通一个能接触到密钥仓库的运维即可。所以我现在做每个项目都会把“密钥生命周期管理”作为一级需求来设计明确每个环节的责任人和审计日志哪怕这意味着流程比以前繁琐。另一个体会是安全配置必须版本化。分区表、SELinux 策略、证书白名单、USB 管控规则这些都要进版本库和固件版本一一对应。我见过太多现场问题最后定位到“某台设备的策略文件是三个月前的手工修改版”这种事故一旦发生几乎没法快速恢复。把策略做成镜像的一部分每次发布都生成唯一的策略指纹设备端和服务端都记录在案才是根治之道。最后一条真机验证不可替代。模拟器上跑得通的安全配置放到真机很可能因为 Bootloader 版本、eFuse 熔丝、外设控制器差异而表现完全不同。尤其是 Secure Boot、dm-verity、SELinux 这几条链路一定要在真实量产固件上做破坏性测试。我在测试 Secure Boot 时遇到过好多次烧录失败还专门做过“假升级真掉电”的模拟实验就为了确认设备在异常路径下不会变砖。这类测试虽然费时但它带来的信心是多少份安全报告都给不了的。如果你正准备给设备做安全加固我建议从这三步开始先把 Secure Boot 和系统完整性校验打开再把文件系统权限和 SELinux 收紧最后补上外设与 USB 管控。每一步做完都跑一轮完整回归测试别急着一步到位。安全是场持久战设备在 Live 状态下的每一分钟都是检验你设计是否扎实的时刻。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价