一批同型号设备共用同一套密钥时,破解其中一台的成本会被均摊到全部设备上。单台的破解难度也许只是”拆开外壳、读一次 flash”,但当它换来十万台设备的身份,投入产出比就变了。设备安全的许多设计,本质是抬高杠杆的另一头。
设备身份:一机一密还是全网共享
设备接入时服务端要回答:连上来的到底是不是我出厂的那台。答案的可靠程度取决于身份密钥的粒度。
| 方案 | 身份唯一性 | 吊销粒度 | 产线要求 | 单台被破波及范围 |
|---|---|---|---|---|
| 全局共享密钥 | 无 | 只能整体更换 | 低 | 全部同型号设备 |
| 按批次分组密钥 | 弱 | 换一批 | 中 | 该批次设备 |
| 一机一密 | 强 | 单台吊销 | 高 | 仅这一台 |
共享密钥的吸引力在于简单:固件里烧同一个值,产线不用逐台个性化,云端也不用注册表;代价是外泄后只能全量换固件,对已部署在现场、网络条件差的设备这常常做不到。
一机一密的额外成本在两处。产线要有安全烧录工位,私钥在设备内生成或由受控编程器注入,烧录数量与出货要对账。云端要有设备注册表,保存公钥或证书、支持吊销,注册动作本身也要有授权。
验证身份用挑战-应答,私钥不上网络:
/* 设备端:用设备私钥对服务端下发的随机数签名,只回传签名值 */
int device_auth(const uint8_t *nonce, size_t nonce_len,
uint8_t *sig_out, size_t *sig_len)
{
return se_sign(SE_SLOT_DEVICE_ID, nonce, nonce_len, sig_out, sig_len);
}
随机数必须一次性,服务端要记录已使用的值,否则签名被截获后可重放。设备没有可靠时钟时,这只能靠服务端缓存保证。
安全启动:信任链逐级传递
安全启动解决”跑起来的代码是不是我签过的”。结构是链式信任:ROM 固化根公钥或其哈希,ROM 验 bootloader,bootloader 验应用,应用再验配置与外挂组件。每一级只负责下一级,信任从不可修改的硬件根往下传。
根公钥哈希通常烧进 eFuse/OTP,一次性写入、无法回改;更换根密钥只能靠有限的备用槽位,槽位用尽即无法再换,这是选型阶段就要确认的事。
三个容易出错的细节:校验必须在加载和执行之前完成,校验的是缓冲区、执行的是另一份映射,两者不严格对应就留下不一致的窗口;校验期间要关掉可能改写缓冲区的 DMA 和外设中断;失败必须进入安全状态,”打个日志继续启动”会让整条链形同虚设。
启动时间会变长,主要来自非对称验签的计算量,在 Cortex-M 这类平台一次验签典型在几十到几百毫秒量级,以实测为准。
固件签名、防回滚与 OTA
签名私钥必须离线,它一旦进了构建服务器就暴露在常规 IT 攻击面里。常见做法是离线签名机或 HSM 保管私钥,构建产物送去签名,设备端只有公钥。
只做签名还不够:攻击者可以把设备降级到”签名合法但有已知漏洞”的旧版本,签名校验会放行。防回滚靠单调版本号,设备保存一个只增不减的计数器,低于它的镜像一律拒绝:
int load_image(const image_hdr_t *hdr, const uint8_t *body, size_t len)
{
if (verify_signature(hdr, body, len) != 0) /* 先验签 */
return -EAUTH;
if (hdr->version < read_rollback_counter()) /* 再比版本 */
return -EVERSION;
write_rollback_counter(hdr->version); /* 然后推进计数器 */
return boot(hdr, body, len);
}
顺序不能颠倒:先推进计数器再验签的话,一段未签名镜像就能把计数器顶得很高,让设备拒绝所有合法固件,保护变成拒绝服务。
A/B 分区让升级失败能切回旧槽,但它与防回滚存在张力:切回的旧版本若低于计数器会被拦住,可行做法是等新版本稳定运行后再推进计数器。增量升级包还要校验基线版本,否则会得到哈希合法但内容错误的镜像。
调试口:出厂前就该关掉的门
UART 输出的启动日志若带密钥片段或 token,等于把信息摆在门口;SWD 更直接,能读写内存和 flash。
处理方式按强度递增:软件关闭,bootloader 里配置选项字节关掉调试端口,实现简单,但攻击者若能让芯片停在 ROM 下载模式就绕过了;熔丝熔断,物理断开、不可逆,现场无法在线调试;调试认证,凭证书或挑战-应答解锁,兼顾可维护性,需要芯片和工具链支持。
代价是现场排查变难,需要配套手段:分级日志、可上报的关键事件、一份带独立授权的售后恢复固件。常见遗漏是正式固件关了,recovery 固件里还开着。
通信层:TLS/DTLS 的开销与证书轮换
TCP 上用 TLS,UDP 上用 DTLS,差异主要在握手对丢包的处理。开销集中在三处:握手的非对称运算、握手所需的往返次数(弱网下可能放大成秒级延迟)、每个连接的收发缓冲区——典型在几 KB 到几十 KB 量级,取决于配置的最大报文长度。对只有几十 KB RAM 的 MCU,并发连接数会被它卡住。
缩减手段包括会话复用与会话票据、用 ECDSA 证书代替 RSA、把密码套件裁到只保留需要的两三个、缩小最大报文长度匹配 MTU。
证书轮换是 IoT 最难的一环。证书有效期按年计,设备部署年限可能十年以上,设备又可能长期断电或处于信号盲区,轮换窗口不可控,失败的后果是设备永久失联。可行做法是留重叠期:新证书下发后新旧同时有效,确认可用且云端接受后才淘汰旧的,轮换提前数月开始。吊销的实时性很难保证,实际更依赖服务端侧的黑白名单。
一个隐蔽的坑是证书校验依赖当前时间。设备没有 RTC 或后备电池掉电后时间归零,证书会因”尚未生效”被判无效,表现为莫名其妙连不上——要么保证可靠时间源,要么给一个宽限窗口交给服务端判断。
密钥放在哪里
| 存储方式 | 抗读取 | 私钥可导出 | 成本 | 适用 |
|---|---|---|---|---|
| 外挂 SPI flash | 极低 | 可 | 低 | 不适合放密钥 |
| MCU 内部 flash + 读保护 | 中 | 绕过即可 | 低 | 成本敏感型 |
| TrustZone / 安全世界 | 高 | 不可 | 中(芯片需支持) | 有硬件隔离的平台 |
| 独立安全元件 / TPM | 高 | 不可 | 高(BOM + 产线) | 高价值设备 |
外挂 flash 拆下来用编程器读,工具成本在几十元级别,几无防护。MCU 内部 flash 配合读保护能挡住大部分低成本读取,前提是调试口已关、选项字节配置正确,且芯片没有公开的绕过方式。
TrustZone 一类硬件隔离的价值在于密钥不出现在非安全世界:业务代码只能通过预定义接口请求签名,拿不到明文,代价是软件架构要拆成两个世界。独立安全元件把生成、存储、运算全放在元件内,私钥从生到死不离芯片,代价是 BOM 和产线个性化流程。
贯穿其中的原则是同一条:优先”请求签名”而不是”读取密钥”。密钥分层也有帮助,根密钥派生身份密钥,会话密钥临时生成、掉电即失。
长期运维的几个边界
产线本身是攻击面:烧录工具、工控机、代工厂掌握的固件与密钥都需要隔离和对账,超出授权数量的部分就是多余凭据。设备报废或转售要有注销动作,服务端吊销身份、本地清除密钥,只做前者的话退役设备仍持有可用私钥。审计日志决定事后能查到什么,至少应上报”固件版本变化””身份校验失败”这类事件。
安全更新是长期承诺,生命周期内要保留推送带签名更新的能力。投入多少取决于设备被攻破后的连带损失,一台联网灯泡和一台门禁控制器的合理投入显然不同,但一机一密、安全启动、关闭调试口这三件的边际成本通常不高。
评论(0)
还没有评论,来抢沙发。
请 登录 后参与评论。