安全擦除、RPMB 与安全启动链路 · 技术探讨
把「数据删干净」「计数不可回退」「代码不可篡改」「根不可伪造」这四件事接成一条链 —— 一份面向嵌入式存储安全的通用原理篇 | 编制日期 2026-10-14 | 版本号 Rev 1.0
结论: 安全擦除、RPMB、安全启动不是三个并列的功能,而是同一条信任链上的四个环节。 安全擦除管的是「密钥没了就等于数据没了」,RPMB 管的是「计数不可回退」, 安全启动管的是「代码不可篡改」,密钥管理管的是「根不可伪造」。 四个环节缺任何一环,整条链就断在那里 —— 而且断掉的那一环决定了前面所有努力的实际价值。
报告总结
存储安全这件事,实践里最常见的问题不是「不会做」,而是做错半步:把覆盖写入当成安全擦除、把加密存储当成防篡改、把签名校验当成固件不可篡改。这些做法单独看都不算错,但它们各自只覆盖了整条链的一段,缺的那一段往往决定了整套方案的实际强度。
这份文档要做四件事:
- 把四个环节接成一条链。安全擦除、RPMB、安全启动、密钥管理不是四个可选功能,而是四个必须同时成立的环节。任何一环缺失,攻击者都不需要攻破其余三环。
- 把三个高频误解讲透。安全擦除不等于覆盖写入、不等于 TRIM;RPMB 是有认证的存储而不是加密存储;做了签名校验但固件区可写,等于没做。
- 把「谁在验证、验证什么、结果放哪」分成三个正交维度。这三个维度混写是绝大多数方案评审文档说不清问题的根源,也是本文反复强调的口径。
- 把故障处置写成可执行的流程。尤其是 RPMB 写失败之后的处置顺序 —— 这是本领域最容易造成不可逆损坏的一类操作。
覆盖范围以 eMMC / UFS / SD 卡的 RPMB 机制为主,同时给出 SPI NAND、带安全子系统的 MCU 以及其他存储介质的等价机制对照(见附录 A)。本文只讲机制与责任划分,不给寄存器地址、位域位置与具体指令数值,器件级参数一律以正式 datasheet 与规范为准。
读者路径
- 第一次系统接触存储安全:按第 1 → 2 章顺序读,先把「哪些数据要保护」和「谁负责什么」立住,这是后面所有讨论的前提。
- 要实现安全擦除:直接看第 3 章(三种擦除手段的适用边界)与第 4 章(密钥层级与删除即销毁),这两章是本文最核心的部分。
- 要用 RPMB 做防篡改或回滚保护:第 5、6 章是重点,尤其 6.4 与 6.5 两节讲清楚了「为什么盲目重试危险」和「必须先认证读回计数」。
- 要做安全启动:第 7、8 章,讲信任链传递与存储布局的耦合关系,核心判断标准是固件区是否真的不可写。
- 要做量产与密钥治理:第 9、10 章,讲凭据注入、派生树、轮换与迁移期双密钥并存。
- 要做攻击面评审或现场排障:第 11、12 章 + 附录 C 的排查决策树,可当手册直接翻。
1. 存储侧的安全需求从哪里来
存储侧的安全需求不是从「数据很重要」推出来的,而是从「攻击者拿到介质之后能做什么」推出来的。这一章把需求拆成可判断的三类资产、一个可量化的威胁模型,以及介质在整体攻击面中的确切位置。
1.1 数据分类:哪些数据丢了要出事
「数据要保护」是一句无法指导设计的话。真正能指导设计的是一个二分问题:这份数据丢了或者外泄了,会不会产生不可逆的损失。按这个问题切分,能得到四个格子,而不是笼统的「重要数据」一类。
第一个维度是外泄之后是否要出事,第二个维度是丢了之后能不能重新生成。两者交叉之后:
- 不可外泄且不可重建——设备唯一密钥、用户口令派生材料。这一格是真正的硬需求,任何妥协都会直接导致设备被克隆或用户身份被仿冒。
- 不可外泄但可重建——固件镜像、配置模板。泄露不直接造成损失,但会显著降低后续攻击的成本,仍然要保护,只是优先级可以靠后。
- 不可篡改但可重建——固件代码、启动配置。这是本文第 7、8 章的主题:数据可以被公开,但一旦能被改就失去了所有安全意义。
- 可丢失——缓存、临时文件、加速数据。只影响体验,不产生不可逆损失,用常规可靠性手段即可。
这个分类的实用价值在于:它能直接回答「这块数据该用哪种机制」。不可外泄且不可重建的走密钥保护,不可篡改的走认证与完整性,不可外泄可重建的走签名加调试口锁定——机制选错的根源往往是分类没做,直接把所有数据一把梭用同一种加密处理。
| 分类 | 典型内容 | 出事的具体表现 | 能否重建 | 对应手段 |
|---|---|---|---|---|
| 不可外泄 · 不可重建 | 设备唯一密钥、认证密钥、口令派生材料 | 攻击者可离线克隆设备或冒充用户 | 不能 | 密钥受保护存放 + 密码学擦除 |
| 不可外泄 · 可重建 | 固件镜像、配置模板 | 泄露后协议细节与实现思路被还原 | 能 | 签名 + 调试口锁定 |
| 不可篡改 · 可重建 | 固件代码、引导代码、启动配置 | 被改后开机即执行攻击者植入的逻辑 | 能 | 完整性校验 + 固件区不可写 |
| 不可篡改 · 不可重建 | 审计日志、计数记录、授权凭据 | 被改后看不出异常操作痕迹 | 不能 | 带认证的存储区(RPMB 等) |
| 可丢失 · 可重建 | 缓存、临时文件、加速数据 | 只影响体验,无不可逆损失 | 能 | 常规删除即可 |
| 可丢失 · 不可重建 | 用户业务数据、采集内容 | 造成用户损失但不产生安全风险 | 不能 | 常规可靠性与备份手段 |
实践中最耗时间的不是实现机制,而是反复争论某块数据「到底要不要加密」。用上面这张表当判定模板,讨论会快很多:先问能不能重新生成,再问被拿到会不会造成不可逆损失,两个答案一填,机制就定了。填完还拿不准的,通常是因为「重建成本」这一项没有定量——这时按「重建需要产线介入且成本高」的保守假设处理即可。
1.2 三类资产:身份凭据、固件代码、用户数据
把上面的分类再按「谁拥有它」聚合,得到三类资产。这三类的保护目标完全不同,混用手段是方案层面最常见的错误:
- 身份凭据——设备唯一密钥、RPMB 认证密钥、证书、用户口令的派生材料。保护目标是不可被复制。一旦被复制,攻击者不需要破解任何东西,直接克隆出一台「合法」设备即可。这是最不容妥协的一类。
- 固件代码——引导代码、固件镜像、配置与校准数据。保护目标是不可被改变。固件本来就是公开的(很多设备甚至公开源码),所以它的安全不依赖保密,而依赖「改不动」。
- 用户数据——业务数据、用户凭据、日志与诊断记录。保护目标是不可被读取。这一类与前两类的关键差别在于:数据量可能很大、生命周期各不相同,因此不能一把钥匙管到底,必须逐文件或逐对象管理密钥。
把三者放在一起看,会发现一个重要结论:身份凭据与固件代码的体量都很小,可以放进受保护的硬件区域;用户数据的体量大且持续增长,只能靠密钥管理来保护内容本身。这个体量差异,直接决定了后面「密码学擦除」这一整套方法论的位置——它是大体量数据唯一能落地的保护方式。
| 维度 | 身份凭据 | 固件代码 | 用户数据 |
|---|---|---|---|
| 核心目标 | 不可被复制 | 不可被改变 | 不可被读取 |
| 被破坏后的表现 | 设备可被克隆或身份被冒用 | 设备行为被攻击者控制 | 隐私泄露与合规风险 |
| 能否重新生成 | 不能,丢了就是永久丢失 | 能,靠发布与烧录流程 | 不能 |
| 需要的保护属性 | 机密性 + 不可导出性 | 完整性 + 来源可验证 | 机密性 |
| 体量特征 | 很小,可放进受保护区 | 较小,但含代码与资源 | 大且持续增长 |
| 失效的最常见原因 | 密钥与数据同域存放 | 固件区其实可写 | 只做了覆盖写入就当作擦除 |
| 本文对应章节 | 第 6 章、第 9 章 | 第 7 章、第 8 章 | 第 3 章、第 4 章 |
1.3 提取攻击与离线暴力破解的威胁模型
存储侧面对的攻击可以粗略归成两类,它们的边界条件完全不同,防御思路也完全不同。
提取攻击的核心是「拿到介质」:攻击者通过拆机、加热、显微操作或直接获得废弃器件,把存储芯片或卡取出后离线分析。这类攻击的边界是物理能力与成本,它不受任何软件限制,也不受重试次数限制。
离线暴力破解的核心是「拿到密文」:攻击者从介质上读出一堆看起来是密文的数据,然后在自己的设备上无限制地尝试密钥,直到解出明文。这类攻击的边界是算力与密钥强度。它的可怕之处在于「无限制」——设备侧的任何「限制尝试次数」在这一层完全无效。
「连续失败 N 次就擦除设备」这类设计,对在线猜测有效,对离线破解完全无效——因为离线时介质已经在攻击者手里,擦除的是他自己手上的数据。所以真正的防线只有两条:让密钥拿不到(受保护存放 + 派生分散),以及让密钥本身足够强。这两条之外的所有努力,都是在延缓而不是阻止。
按能力把攻击者分档,有助于判断某项防御是否值得投入。下表给出四个典型档位,以及每一档攻击者在物理层之上「拿不到什么」——这才是设计防御时真正该瞄准的地方。
| 档位 | 典型手段 | 能做到什么 | 拿不到什么 | 应有的最低防线 |
|---|---|---|---|---|
| 消费级 | 常规读卡设备、拆机、废弃件 | 读出可读区域与明文数据 | 熔丝状态、器件内部密钥 | 密码学擦除 + 固件区写保护 |
| 实验室级 | 专用读写器、逻辑分析仪、抓包 | 复现协议流程、观测密钥协商 | 不可导出的根密钥 | 认证存储区 + 安全启动 |
| 专业级 | 热修改、探针卡、故障注入 | 绕过部分状态检查、观测片内总线 | 单向计数器与已熔断的熔丝 | 控制器内完成认证 + 熔丝锁定 |
| 系统性投入 | 长期定向研究、定制手段 | 积累的后门、侧信道与长期观测数据 | 理论上没有拿不到的东西 | 分层防护 + 定期轮换 + 缩短暴露期 |
这张表的实际用法是反向确认:从自己产品当前状态的最高一档往下看,如果某一档的「应有最低防线」在自己的产品里是空的,那就是一个待补的缺口,而不是一个「暂时用不上」的高配项。最后一档永远防不住,所以设计目标不是「不可战胜」,而是把每一档的成本都抬高到不值得。
1.4 存储在攻击面中的位置
把一次攻击拆成四层来看,存储层的位置非常特殊:它是唯一一个攻击者可以完整带走、并且可以无限次离线分析的层。
物理层、接口层、协议层的攻击都发生在设备仍然受控的场合——它们可以被限速、被检测、被拒绝,甚至可以在检测到之后主动销毁数据。而一旦介质脱离设备,攻击者就获得了完整的时间优势与算力优势:可以慢慢试、可以反复解剖、可以对比同一批次的多个样本。此时任何依赖「设备在线时状态」的防御都失效。
这解释了为什么很多传统安全设计在嵌入式存储上不适用:在服务器场景里,「检测到异常就擦除数据」是一种有效策略,因为攻击者必须持续在线;在嵌入式设备里,介质会被直接拿走,这条策略的价值大幅下降。
| 层次 | 攻击者能做什么 | 存储层能否提供防护 | 说明 |
|---|---|---|---|
| 物理层 | 拆封、显微读取、把介质带走离线分析 | 能,但代价最高 | 这一层失守后,所有上层机制同时失效 |
| 接口层 | 监听总线、抓取命令、注入伪造响应 | 部分能 | 只保护经由器件完成的访问,管不到物理直读 |
| 协议层 | 复现握手流程、构造非法请求与非法状态 | 能 | 认证与计数的强度决定这一层的天花板 |
| 主机软件层 | 调用合法接口、传合法参数做非法事 | 基本不能 | 合法调用不等于合法用途,要靠主机侧策略补 |
1.5 存储安全不等于接口安全
「传输通道是加密的,所以数据是安全的」是硬件项目里非常常见的一句想当然。这句话混淆了两个完全正交的保护对象:
接口安全保护的是传输过程——数据在总线上跑的那一刻有没有被监听、截获、替换。它由协议层机制承担,与介质上留着什么没有任何关系。存储安全保护的是静态留存——数据落在介质上之后,攻击者拿到介质能不能读出来。它由器件与主机侧机制承担,与传输时是否加密也没有必然关系。
两种组合都有明确的失效场景:
- 只做接口安全:传输过程看不出问题,但攻击者一旦拆机读介质,所有内容一览无余。对于「攻击者能接触物理设备」的产品,这等于没有保护。
- 只做存储安全:介质上的内容确实不可读,但攻击者可以在传输过程中替换、注入、伪造后续的响应——比如在设备请求认证时返回一个「通过」的结果。对协议状态机有篡改能力的攻击者,这比破解密文便宜得多。
正确的判断标准是:先问攻击者最可能走哪条路,再决定在哪一层投入。产品会出厂到客户手上,物理接触几乎必然发生,那么存储安全是底线;如果产品还暴露在不可信网络上被直接访问,那么接口安全同样不能省。两者不是二选一。
| 维度 | 存储安全 | 接口安全 |
|---|---|---|
| 保护对象 | 介质上长期留存的静态内容 | 传输过程中的数据 |
| 典型失效场景 | 介质被拿走、拆解、离线分析 | 链路被监听、被中间人篡改 |
| 常见手段 | 密码学擦除、认证存储区、分区权限 | 通道加密、消息认证、重放保护 |
| 覆盖不到的地方 | 主机运行时内存中的明文 | 介质上已经落盘的旧内容 |
| 失效后的补救 | 无法补救,只能重建设备身份 | 可以靠密钥轮换与通道重建恢复 |
| 典型误解 | 以为加密了就不用管删除的事 | 以为链路安全就等于数据安全 |
1.6 本文要回答的问题与主线
综合前五节,存储侧的安全需求可以收敛成五个问题。本文按这五个问题的顺序推进,章节与问题的对应关系如下:
- 哪些数据必须保护,用什么属性保护?(第 1 章)——先做分类,避免机制选错。
- 谁负责验证,验证结果放在哪里?(第 2 章)——把器件、控制器、主机三层的责任边界划清。
- 数据删掉之后怎么才能保证不可恢复?(第 3、4 章)——三种擦除手段的适用边界,以及密码学擦除的工程实现。
- 计数与版本怎么保证不回退?(第 5、6 章)——RPMB 的认证模型、帧结构、计数语义,以及最危险的写失败处置。
- 代码怎么保证不被改、根密钥怎么保证不被伪造?(第 7~10 章)——信任链、存储布局、主机侧凭据治理与轮换。
这五个问题串起来就是本文的主线:一条从根密钥到用户数据的信任链。链条的起点是「根密钥不可伪造」(第 7、9 章),中间是「代码不可篡改」(第 7、8 章)和「计数不可回退」(第 5、6 章),终点是「密钥没了就等于数据没了」(第 3、4 章)。任何一环断开,链条的强度就等于最弱那一环,而不是最强那一环。
- 安全擦除不等于覆盖写入,也不等于 TRIM。密码学擦除的强度来自「密钥被销毁」,而不是「数据被抹掉」——这是本文第 3 章要讲透的第一件事。
- RPMB 是有认证的存储,不是加密存储。它防篡改、防重放,不防偷看。把这两件事分清,才不会在方案评审时既高估又低估它。
- 安全启动与存储的耦合点在「不可写」。固件区的写保护强度决定整条链的强度;只做签名校验但固件区可写,等于没做。
2. 三条安全边界与责任划分
安全不是一件事,而是三件层次完全不同的事各自的承诺。器件内部、控制器、主机软件各自只能对「自己能验证的东西」负责——本章把三条边界划清,并给出一个反复使用的三维描述口径。
本章最重要的工具不是那几个表格,而是最后那三个维度。讨论任何一项安全机制时,都应该先问三个问题:谁在验证、验证的是什么、验证结果放在哪里。这三个维度互相正交,混在一起说就会得出「器件做了完整性校验」这种既不知道谁做、也不知道校验什么的糊涂结论。
- 谁在验证——器件内部(阵列与一次性区)、控制器(逻辑块与状态机)、主机软件(应用与引导代码)。三者的可信任程度依次递减,可被篡改的入口依次递增。
- 验证什么——完整性(内容没被改)、真实性(内容确实来自持有密钥的一方)、新鲜性(内容不比上次旧)。三者对应三种不同的机制,经常被笼统地混称为「校验」。
- 验证结果放哪——熔丝或一次性位(不可逆)、受保护存储区或单调计数器(可读可增)、普通可写分区(可被改,只有参考价值)。这一项决定验证结果到底算不算数。
2.1 器件内部边界
器件内部边界指的是介质自身提供的、脱离任何外部控制也无法绕过的那些保证。它是整条链里强度最高的一层,因为攻击者要绕过它必须做物理破坏,而物理破坏同时也会销毁他想要的数据。
这一层能提供的保证有四类:内容认证(只有通过了认证的请求才会改变受保护区域)、不可逆状态位(只能由未置位变为已置位,无法恢复)、密钥不外流(密钥在芯片内参与运算,不通过外部总线输出明文)、以及单调计数(某些区域提供的计数器只能递增)。
这一层明确不保证的事情同样重要:它不关心数据取出去之后被怎么使用,不判断上层的策略是否合理,也不保证上层会在正确时机使用这些机制。器件提供能力,策略由主机决定——这是边界的一句话总结。
| 项目 | 器件能保证的 | 器件不能保证的 |
|---|---|---|
| 内容认证 | 通过认证的请求才会被接受 | 上层是否发出了正确的认证请求 |
| 一次性位 | 只能由未置位变为已置位,不可逆 | 是否在正确的时机置位 |
| 根密钥 | 密钥在芯片内参与运算,不经总线输出 | 芯片被物理攻击时的安全性上限 |
| 单调计数 | 受保护计数器只能递增不可回退 | 计数被读出之后如何决策 |
| 失败日志 | 记录认证失败次数并允许上层读取 | 上层是否据此做出正确响应 |
| 边界位置 | 在数据离开芯片之前 | 数据离开芯片之后全部失效 |
2.2 控制器边界
控制器边界指的是器件内部控制器对主机侧暴露的那一层行为约束。它比器件内部边界弱(因为它要处理来自不可信主机的请求),但覆盖范围更广(它管的是整个逻辑块视图,而不只是某一个区域)。
这一层的典型能力包括:按分区或按块的访问权限判定(哪些区域主机可以写、哪些只读、哪些根本不可见)、认证写(受保护区域只接受通过认证的写请求)、请求格式与状态机检查(拒绝非法状态转换)、以及受保护的计数器与序列号。
它不保证的是策略层面的正确性。举个最能说明问题的例子:写失败之后重试是最自然的应用层行为,但控制器只能告诉你「失败了」,重试是否安全完全由主机判断。把这个判断推给控制器的设计,等于把整条链的强度交给了控制器作者。
| 项目 | 控制器能保证的 | 控制器不能保证的 |
|---|---|---|
| 访问控制 | 按分区或按块判定读写权限 | 主机是否自觉遵守权限约定 |
| 认证写 | 认证通过才更新受保护区域 | 认证所用的密钥是否已经泄露 |
| 状态机检查 | 拒绝非法状态转换与格式错误的请求 | 上层状态机本身是否设计正确 |
| 计数与序列 | 拒绝计数不匹配的请求与重复序列 | 重试策略是否安全 |
| 读保护 | 受保护数据只能通过认证读取 | 读出之后如何保管与使用 |
| 边界位置 | 在主机命令与介质访问之间 | 主机应用侧与物理接口侧都管不到 |
2.3 主机软件边界
主机软件边界指的是运行在主控上的代码所能提供的保证,包括引导代码、驱动、文件系统与应用。它的特点是灵活但可信度最低——它运行在攻击者最终一定能够接触到的设备上,任何「只有生产时才存在」的假设都不成立。
这一层实际承担的工作量最大:密钥派生(把根密钥按用途展开成每一份数据对应的密钥)、擦除策略(决定什么时候销毁哪把密钥)、启动校验顺序(决定先验什么后验什么)、凭据生命周期管理(生成、注入、备份、轮换、销毁)、以及故障处置(认证失败时停止还是重试)。
它不保证的也很有代表性:不保证攻击者拿着完好设备慢慢试的时候能被挡住。主机侧能做的是缩短暴露时间、提供可观测性、并配合熔丝与派生树把「拿到一台设备」和「拿到所有设备的根密钥」彻底分开。
| 项目 | 主机能保证的 | 主机不能保证的 |
|---|---|---|
| 密钥派生 | 按用途派生并在使用后清除中间量 | 派生算法内部是否已被攻破 |
| 擦除策略 | 删除文件时同步销毁对应密钥 | 介质物理层面是否仍有残留 |
| 启动校验 | 按顺序校验并拒绝不合格镜像 | 不可写区域是否真的不可写 |
| 凭据治理 | 保证每台设备的凭据唯一且可追溯 | 生产流程本身是否被动手脚 |
| 故障处置 | 识别认证失败并停止危险的自动重试 | 硬件是否已被物理破坏 |
| 边界位置 | 在应用逻辑与控制器命令之间 | 对持有整机的攻击者几乎无约束力 |
2.4 三层各自能保证什么
把三层的保证按前面说的三个正交维度摊开,会得到一张比「三层能力表」更有用的矩阵。这张矩阵的价值在于:任何一个安全特性,都能在这张表里找到它唯一的、不可替代的位置。找不到位置的特性,通常说明它的必要性存疑。
举三个具体例子说明怎么用这张表定位问题:
- 「我们要不要上 RPMB?」——查第一行。RPMB 的验证者是控制器,验证的是真实性与新鲜性,结果放在受保护计数上。如果需求只是「防止内容被改」,那需要一个验证完整性且结果放在不可篡改位置的东西,RPMB 只是其中一种实现。
- 「固件区设成只读能不能算安全?」——查第三行与第三列。主机软件层的验证结果放在普通可写分区里只有参考价值,因为攻击者改的往往就是那一区。所以「只读」必须由控制器或器件层面的权限来保证,而不是靠上层不写。
- 「密钥派生能不能放在控制器里做?」——可以,但派生算法与输入一旦在控制器里,就要接受控制器安全等级的约束。放在主机侧做,算法可以更快迭代,但主机侧本身不可信。这个取舍属于架构决策,不属于机制细节。
| 验证者 | 验证什么 | 验证结果放在哪 | 覆盖不到的 |
|---|---|---|---|
| 器件内部 | 内容是否由持有密钥的一方产生 | 一次性位或熔丝(不可逆) | 内容被取走之后的使用 |
| 控制器 | 访问者是否持有密钥、计数是否匹配 | 受保护存储区与单调计数器 | 主机侧的策略是否正确 |
| 主机软件 | 策略是否被遵守、顺序是否正确 | 受保护分区、熔丝、日志告警 | 物理攻击与生产流程绕过 |
一个常见的设计错误是:主机软件校验通过之后,把「已通过」这个标志写进一个普通可写分区,然后后续流程信任这个标志。这个设计的问题不在于校验逻辑,而在于标志的存放位置与攻击者的能力不匹配——如果攻击者能改那块分区,他就能把失败改成通过。
判断标准很简单:验证结果存放位置的写权限,必须不劣于被验证对象本身。被验证对象在只读区,结果也必须在不可写或受保护的位置;结果在可写区,就等于没有验证。
2.5 责任错配的典型后果
把某一层该做的事推给另一层,就叫责任错配。它的共同特征是:每一层单独看都在正常工作,合起来却留了一个谁都没管的缺口。下面六种是实践中最常遇到的。
| 错配 | 典型想法 | 实际后果 |
|---|---|---|
| 把完整性寄托在加密上 | 数据加密了就没人能改 | 密文可被任意篡改,解密后得到被改过的内容 |
| 把认证寄托在签名上 | 验签通过就说明固件是好的 | 固件区可写时,攻击者直接改代码绕过验签 |
| 把权限寄托在主机上 | 主机不主动写就不用管权限 | 任何能发命令的一方都能读受保护数据 |
| 把计数寄托在重试上 | 失败就重试,总有一次会成功 | 写计数已被推进,重试永远对不上 |
| 把安全寄托在内存清零上 | 运行时清零就安全了 | 内存内容可被转储,密钥仍会外泄 |
| 把产线安全寄托在流程 | 流程上没人会破坏它 | 凭据被批量复制到多台设备 |
这六种错配里,第四种(把计数寄托在重试)后果最不可逆,因为它破坏的是单调性——而单调性一旦被破坏,无法通过后续操作恢复。第 6.4 节会用一整节讲这件事,本章只强调一点:凡是带计数或序列的机制,都必须先明确「失败之后该做什么」,而不是留到现场再决定。
另外值得注意的是,表格里最后两种(内存清零、产线流程)都属于「攻击成本低、后果严重」的类别。它们的共同点是:防护措施看起来做了,实施过程却完全不受控。安全属性的强度等于最弱的那个执行环节,而不是设计文档上写的那个值。
2.6 威胁模型到机制的对照
本章最后把前面所有讨论压缩成一张对照表:每一条威胁,写清它在第几层被挡、谁去挡、验证的是什么属性、结果存在哪。这张表也是后续各章的索引——第 3 章讲表中的「介质被提取后离线分析」一行,第 5、6 章讲「事务重放」与「版本回滚」两行,第 7、8 章讲「代码篡改」与「根密钥伪造」两行。
| 威胁 | 在哪一层被挡 | 机制 | 验证的属性 | 结果存放位置 |
|---|---|---|---|---|
| 介质被提取后离线分析 | 器件内部 + 主机软件 | 密码学擦除 + 密钥受保护存放 | 机密性(密钥是否可得) | 受保护密钥区 |
| 密钥被离线暴力破解 | 主机软件 | 强密钥派生 + 每文件密钥分散 | 机密性(密钥是否够强) | 密钥版本与派生树 |
| 固件代码被篡改 | 控制器 + 主机软件 | 固件区不可写 + 签名校验 | 完整性 + 真实性 | 不可写分区 |
| 旧版本镜像被回滚 | 器件内部 + 控制器 | 版本计数器(熔丝或受保护计数) | 新鲜性(版本是否更新) | 熔丝位或单调计数 |
| 认证事务被重放 | 控制器 | 认证帧 + 序号 + 随机数 | 新鲜性(是否本次会话) | 受保护计数 |
| 根密钥被伪造 | 器件内部 | 一次性可编程的信任根 | 真实性(是否官方产出) | 熔丝 / 一次性区 |
| 密钥泄露后被长期复用 | 全链 | 轮换 + 版本迁移 + 双密钥并存 | 新鲜性(密钥版本是否最新) | 密钥版本号 |
请注意最后三行的共同特征:它们验证的都是新鲜性。新鲜性无法靠「算得对不对」来保证,只能靠不可回退的存储状态来保证——这就是第 5、6 章要用整整两章篇幅讲 RPMB 的原因。对于嵌入式设备而言,「上一次的状态」必须存在一个攻击者改不回去的地方,否则所有版本判断、计数判断、重放判断都只是应用层的一个变量,随时可以被改。
3. 安全擦除:让数据真正不可恢复
这一章要纠正本文的第一个高频误解:安全擦除不等于覆盖写入,也不等于 TRIM。三种手段都能让数据「看起来没了」,但只有一种能让攻击者即使拿到整颗介质也读不出来。
3.1 常规删除为什么留着痕迹
文件系统里的删除,绝大多数情况下只做了两件事:把文件占用的块标记为空闲,把目录项标记为无效。从物理层面看,数据块里的每一个比特都还留在原地,一个都没变。
这不是实现缺陷,而是有意为之的设计。原因很直接:真正的数据搬运代价很高。在闪存介质上,一次「读出旧块 → 写入新块 → 标记旧块空闲」的完整回收可能带来数百微秒到毫秒级的延迟,还伴随写放大。如果每次删除小文件都做完整回收,删除操作的性能就无法接受。
由此产生两个直接后果:
- 数据可以通过读工具直接还原。只要拿到介质(或者能访问它的接口),跳过文件系统直接读物理块,就能看到完整的原内容。对攻击者来说这几乎是零门槛的操作。
- 文件系统自身也可能泄露结构信息。目录树、文件名、分配表、访问时间戳这些元数据即使在「删除」之后仍可能残留,进一步降低攻击者的分析成本。
因此,「文件删掉了」在安全语境下是一句几乎没有信息量的话。真正要问的是:删掉之后,那份数据在介质上还能不能被读出来。
| 删除动作 | 物理层面发生了什么 | 数据可否被还原 | 残留物 |
|---|---|---|---|
| 删除文件 | 目录项失效、块被标记空闲 | 可以,读物理块即得原文 | 数据本体 + 元数据片段 |
| 清空回收站 | 标记删除,但块通常未回收 | 可以,回收站清空不等于回收 | 数据本体仍在 |
| 文件系统格式化 | 重建元数据结构 | 可以,格式化通常不覆盖数据区 | 数据区内容基本完整 |
| 整盘覆写为零 | 逐块写入固定模式 | 多数场景可以,取决于覆盖是否彻底 | 磨损均衡可能映射到旧块 |
| 删除分区表 | 分区索引被清除 | 可以,未触及数据区 | 数据区全部保留 |
| 低层物理擦除 | 擦除单元被真实擦除 | 不可还原(理想情况下) | 仅剩器件内部的残余电荷分布 |
3.2 覆盖写入的真实效果与局限
覆盖写入的思路很直白:在旧数据的位置上写入新内容,理论上旧内容就被「盖掉」了。这个思路在机械介质上曾经是标准做法,但在闪存介质上存在四个层次不同的问题。
第一,闪存不能原地覆盖。闪存的物理特性决定了不擦除就无法把某一位从 1 写成 0 之外的状态。要写入新数据,必须先擦除再写入,而擦除的粒度远大于写入。这导致覆盖操作实际上变成了「读出 → 改写 → 擦除目标块 → 写入」的循环,中间状态是旧数据仍然存在的。
第二,磨损均衡会破坏位置对应关系。逻辑块与物理块之间存在映射。当某个物理块即将写满时,控制器会做垃圾回收,把有效数据搬走、擦除旧块。此时「旧内容所在的物理位置」不再对应你当初执行覆盖的那个逻辑块。你覆盖了逻辑块 A,但旧内容的物理副本可能已经被搬到了别的位置。
第三,FTL 日志与映射表本身会留痕。控制器为了支持掉电保护,会把映射关系的变化写入日志结构。这些日志记录里可能包含旧块号、新块号、时间信息,有时还会包含数据的校验信息。日志区的数据不一定被覆盖。
第四,写入过程本身可能失败。如果覆盖写到一半掉电,旧数据可能已经被部分擦除、部分保留,形成的状态既不是原文也不是新文,但仍可能包含可恢复的信息片段。
结论是:覆盖写入只能提高恢复难度,不能消除恢复可能。它适合用来处理「敏感数据不能长期留在介质上」这一合规要求,但不足以对抗有备的提取攻击。
在很多需求文档里,「数据删除后需要不可恢复」这句话会被直接实现为「删除前先覆写零」。这在合规检查中可能能通过,但在对抗提取攻击的场景里,它挡不住任何具备基本能力的攻击者——因为攻击者不需要攻破覆写逻辑,他只需要在覆写之前就把介质读走一份。
更值得注意的是,覆写本身会显著消耗介质寿命。对大容量数据区做全量覆写,写放大可能达到数十倍,这是一笔实实在在的成本。理解了这一点,就更容易接受「不动数据、只动密钥」的思路。
| 局限 | 成因 | 实际后果 | 能否靠流程规避 |
|---|---|---|---|
| 无法原地覆盖 | 闪存擦除粒度大于写入粒度 | 覆盖过程包含读改写中间态 | 不能 |
| 旧位置被改变 | 磨损均衡与垃圾回收搬迁数据 | 覆盖逻辑块不等于覆盖物理副本 | 不能 |
| 日志残留 | 掉电保护需要记录映射变化 | 日志区可能含旧块号与校验信息 | 不能 |
| 写入可能中断 | 掉电、复位、器件异常 | 产生既非原文也非新文的混合态 | 只能降低概率 |
| 寿命代价 | 覆写造成额外擦除与写入 | 写放大数十倍,加速介质老化 | 不能 |
3.3 擦除命令与物理擦除
器件与协议层面通常提供了一类显式的擦除或置无效操作:对闪存介质是擦除单元与块擦除类命令,对块设备是 TRIM / DISCARD 类命令,对安全存储区则有专门的失效或清零流程。它们与「删除文件」的差别在于:它们作用在介质层面,而不是文件系统层面。
这类操作的关键特征是「把块标记为无效,并让器件在合适的时机真正擦除它」。注意这里的措辞——时机由器件决定,不由主机决定。主机能做的只是发出通知,然后由器件在后台完成真正的物理擦除。
这个「延迟」特性带来两个必须理解的后果:
- 擦除完成之前,数据仍然可读。如果攻击者在擦除窗口内把介质拿走,仍然能读出内容。在长时间不写满的分区里,这个窗口可能很长。
- 返回成功不等于物理擦除已完成。命令返回成功通常只表示「标记成功」,不表示「物理擦除完成」。把返回值当成擦除完成的凭据,是一个常见误判。
因此,物理擦除的定位应当是:它能防止旧数据在未来被偶然读到,也能减少介质上的信息残留量,但它的时间不可控,无法作为对抗提取攻击的主要手段。真正的保证必须来自「即使数据还在,也读不出来」。
| 操作层次 | 作用对象 | 时机 | 对提取攻击的作用 |
|---|---|---|---|
| 文件系统删除 | 目录项与块分配表 | 立即 | 基本无作用 |
| 块设备 TRIM / 失效通知 | 逻辑块到物理块的映射 | 立即标记 | 降低作用,取决于回收时机 |
| 闪存块擦除 | 物理擦除单元 | 由器件调度 | 中等,受回收时机限制 |
| 芯片内部安全擦除 | 受保护区域 | 由器件执行 | 较强,且受认证保护 |
| 密码学擦除 | 密钥,而非数据 | 由主机控制,立即生效 | 强,不依赖物理擦除时机 |
3.4 密码学擦除:密钥即销毁
密码学擦除的思路与前三种完全不同:它不碰数据,只碰密钥。数据以密文形式原样留在介质上,不做任何搬运、不触发额外擦除、不消耗寿命;只要对应的密钥被销毁,那份数据就永久不可读了。
它的强度来自一个非常朴素的性质:在计算能力有限的条件下,没有正确密钥的密文与随机数据不可区分。这不是工程经验,而是密码学的基本假设。攻击者把整颗介质读走、反复分析、用最好的设备去尝试,得到的都只是看起来随机的字节。
这里必须把一个高频误解讲透:密码学擦除的强度来自「密钥被销毁」,而不是「数据被抹掉」。这两句话在工程文档里经常被混用,但它们对应的是完全不同的实现与完全不同的成本:前者改的是密钥存储区的一小块内容,一次写入即可,且不消耗介质寿命;后者要处理整个数据区,写放大巨大且时机不可控。
密码学擦除的全部强度建立在一个前提上——密钥本身不可恢复了。这个前提在实践中经常被破坏:
- 密钥被「删除」了,但只是改了个文件属性,密钥本体还在介质上;
- 密钥销毁了,但旧密钥被备份到了别处(快照、镜像、导出文件);
- 密钥销毁了,但多份相同密钥的副本还有一份活着;
- 密钥是可从根密钥确定性派生的,而根密钥还在——那么「销毁文件密钥」只是删了一个中间结果,下次派生又会算出来。
最后一条尤其隐蔽:如果文件密钥是从主密钥确定性派生的,那么「销毁文件密钥」必须同时让这次派生不可再现,否则攻击者拿到主密钥就能重算出所有历史文件密钥。第 4 章会用一整节讲这件事。
| 维度 | 覆盖 / TRIM / 物理擦除 | 密码学擦除 |
|---|---|---|
| 作用对象 | 数据本体 | 密钥 |
| 介质寿命消耗 | 显著,取决于数据量 | 极小,一次写入量级 |
| 生效时机 | 可能延迟,由器件调度 | 立即,密钥写入即完成 |
| 对抗物理提取 | 弱到中等 | 强,密文与随机数据不可区分 |
| 能否撤销 | 不能,被擦除即消失 | 在销毁前可以,重建密钥即可恢复 |
| 失败模式 | 擦除未真正执行 | 密钥仍有副本可读 |
| 适用体量 | 小体量或受监管字段 | 大体量数据的主要手段 |
3.5 TRIM 与安全擦除的关系与差别
TRIM 是主机与块设备之间的一个通知机制:主机告诉存储设备「这个逻辑块的内容你可以丢弃了」。它的设计目标是提升性能与寿命,而不是提升安全性。
把 TRIM 当成安全擦除,源于一个看起来很自然的推理:「我告诉设备这块数据不要了,设备又真的擦掉了,所以数据没了」。这个推理有三处不成立:
- 目的是回收,不是抹除。回收的判定标准是「这块数据还有没有有效引用」,而不是「这块数据是否敏感」。对于文件系统层面的删除,块在被回收之前一直处于「有效」状态。
- 时机不可控。回收发生在器件认为合适的时刻,可能是一段时间之后,也可能因为频繁写入而很快。更麻烦的是,如果后续又有数据被分配到同一块,被回收的旧内容与新内容可能并存于同一物理块。
- 粒度是块,不是文件。TRIM 通知的最小单位是块。一个文件的数据可能分布在若干块里,也可能与别的文件共用块。块被回收时,回收的是整块内容,与「某个文件已删除」这件事并不严格对应。
所以准确的关系是:TRIM 是回收通知,不是安全擦除;物理擦除是 TRIM 的可能后果,但副作用与时机都不为安全而设计。两者可以配合使用——密码学擦除保证「读不出来」,物理擦除减少「留在介质上的信息量」,但两者绝不能互相替代。
| 对比项 | TRIM / 失效通知 | 安全擦除(密码学) |
|---|---|---|
| 设计目的 | 提升性能与介质寿命 | 保证数据不可恢复 |
| 通知粒度 | 块 | 文件或对象 |
| 执行方 | 存储设备 | 主机软件 |
| 生效时机 | 由设备调度,不确定 | 密钥写入即刻生效 |
| 能否撤销 | 不能,回收即丢弃 | 销毁前可撤销 |
| 失败模式 | 未及时回收、旧内容与新内容并存 | 密钥存在可恢复副本 |
| 在本文中的定位 | 减少残留的辅助手段 | 对抗提取攻击的主要手段 |
3.6 三种擦除手段的适用边界
把三种手段放在一起,可以得到一个清晰的分工:密码学擦除承担主要的安全责任,物理擦除承担残留控制,TRIM 承担回收效率。三者不是替代关系,而是不同维度上的补充。
在实际产品里,合理的组合通常是:删除文件时销毁文件密钥(立即生效的安全保证)+向存储层发出块失效通知(减少残留)+ 由器件在后台完成物理擦除(长期残留控制)。三步的顺序有讲究:密钥销毁必须在最前面,因为它是唯一一步能立即生效且不依赖器件调度的一步。
需要特别注意的是三类手段都不适用的场景:
- 运行时内存中的明文。数据在使用期间以明文存在于内存或缓存中,介质层的任何擦除手段都管不到。这一块要靠内存保护、零化、以及尽量缩短明文驻留时间来处理。
- 快照与备份中的副本。介质被擦除,但快照还在另一个池子里。这是第 4.5 节专门讨论的问题。
- 被导出的数据。数据曾经通过某个功能导出到别处(诊断日志、崩溃转储、工厂测试镜像),介质上的擦除完全管不到。这属于流程问题,不是技术问题。
| 数据类型 | 推荐手段 | 理由 | 需要注意 |
|---|---|---|---|
| 用户业务数据(大体量) | 密码学擦除为主 | 唯一能做到立即生效且不耗寿命的手段 | 必须配合快照与备份的同步销毁 |
| 固件与配置(小体量) | 物理擦除 + 不可写保护 | 体量小,擦除代价可接受 | 写保护比擦除更重要 |
| 日志与诊断数据 | 物理擦除 + 保留期限制 | 需要物理可见性用于排障 | 严格区分可导出字段 |
| 临时文件与缓存 | 常规删除即可 | 无敏感信息,重建成本低 | 不要为省事给临时数据也加密 |
| 运行时内存明文 | 内存零化 + 缩短驻留 | 介质手段无效 | 崩溃转储路径常被忽略 |
| 导出的诊断文件 | 流程管控 | 介质擦除管不到 | 要明确哪些字段允许导出 |
4. 密码学擦除的工程实现
第 3 章讲清了「为什么要销毁密钥」,这一章讲「怎么把密钥组织起来,才能既安全又可维护」。这一章的每一条都是第 6 章与第 9 章的直接前提。
4.1 每文件密钥的密钥层级
「每文件密钥」这个做法听起来简单,实现起来有一个必须先回答的问题:每把文件密钥从哪里来。如果所有文件密钥都从一个全局密钥直接派生,那么销毁其中一把并不影响派生机制本身,攻击者拿到全局密钥就能重算出所有历史密钥。要避免这一点,就必须建立层级。
一个可用的层级通常有三到四层:
- 根密钥——设备唯一,出厂时注入,永不销毁,永不离开受保护区。派生树的所有起点。
- 主密钥——按用途从根密钥派生,比如「用户数据主密钥」「系统配置主密钥」「通信主密钥」。用途分离的意义在于:某个用途的密钥出问题,不会波及其它用途。
- 文件密钥——从对应的主密钥派生,每文件一把,与文件标识(通常是文件名或其哈希)绑定。这一层是可销毁的、可轮换的、也是擦除动作真正作用的对象。
- 数据本体——用文件密钥加密后的密文。它从不移动、不被擦除。
这个层级的关键性质是:销毁文件密钥只影响这一个文件,不影响任何其他文件,也不影响派生树的其余部分。这正是「删一个文件就等于删一份数据」得以成立的结构基础。
| 层级 | 数量关系 | 存放位置 | 可否销毁 | 泄露后果 |
|---|---|---|---|---|
| 根密钥 | 每设备一把 | 器件受保护区,永不输出 | 不可销毁 | 全部数据一次性暴露 |
| 主密钥 | 每用途一把 | 受保护区或主机安全存储 | 不可单独销毁 | 对应用途的数据全部暴露 |
| 文件密钥 | 每文件一把 | 密钥表或文件系统元数据 | 可销毁,擦除的对象 | 仅该文件暴露 |
| 数据本体 | 每文件一块 | 普通数据区 | 不销毁 | 无密钥则无信息 |
4.2 密钥的存放位置与保护
文件密钥本身也是数据,也要被攻击者看到。所以「密钥存哪」这个问题,与「数据存哪」同等重要——而且更容易被忽略,因为密钥通常很小、体量不成问题,容易被随手放在一个「看起来还行」的位置。
存放位置的选择本质上是一个权衡:越安全的位置越难被快速读取,越容易被读取的位置越适合处理大量密钥。常见的可用位置大致有四类:
- 器件的受保护区域——最强,但容量极小,通常只能放根密钥与少量主密钥。文件密钥放不下这么多。
- 主机侧的安全存储——比如密钥存储服务、受保护的 NVM 区域。可以放较多密钥,且有访问控制。
- 密钥表(存放在受保护分区内)——文件密钥集中存放,靠分区权限保护。这是常见的折中,但要注意:密钥表所在分区必须与数据分区同等或更严格的保护级别。
- 文件系统元数据 / 扩展属性——与文件绑定,紧凑且便于生命周期管理。但元数据区的保护级别通常弱于数据区,这是一个真实风险点。
最常见的错误是把密钥表放在与数据相同的保护级别下。如果攻击者能读数据区,通常也能读到密钥表,那加密就完全白做了。判断标准很简单:密钥表的可读权限必须严格小于等于数据区的可读权限。
如果攻击者能够拿到介质上除根密钥之外的全部内容(包括所有密钥表、所有密文、所有元数据),那么整个方案的安全性就完全建立在「根密钥不可得」这一条上。这不是缺陷——这正是正确的设计结构。但它要求你确认:根密钥确实拿不到,而不是「理论上应该拿不到」。
具体要确认三件事:根密钥是否在受保护区域内、该区域是否真的不可读、以及受保护区域的状态是否被正确锁定过。第三条经常出问题——设备出厂时保护位没置上,整套机制就从未真正生效过,而软件侧的日志里一切正常。
4.3 密钥派生与轮换
派生解决的是「如何从一把主密钥得到无数把文件密钥」,轮换解决的是「主密钥变了之后怎么办」。这两个机制必须一起设计,否则其中一个一定会出问题。
派生设计的核心要求是三条:确定性(同样的输入必须算出同样的密钥,否则文件就打不开了)、可绑定(派生必须绑定文件标识,否则同一主密钥派生出的一批密钥之间会互相相关,一处泄露波及全部)、以及单向(从派生结果无法反推出主密钥)。第三点通常由成熟的密钥派生函数保证,不需要自己设计。
轮换设计的核心问题是:轮换之后,旧数据怎么办。因为数据是用旧密钥加密的,主密钥一旦轮换,旧密钥就消失了,所有历史数据都会变成不可读的垃圾。可行的做法有三种:
- _lazy 重加密(也称惰性更新):文件在被访问时顺手用新密钥重新加密。改动最小,但没被访问的旧数据会一直是旧密钥加密的。
- 批量重加密:一次性把全部数据用新密钥重写。彻底但代价大,且过程中一旦中断就麻烦。
- 双密钥并存:同时保留新旧两把密钥,新数据用新密钥,旧数据保留密钥索引,访问时按索引选密钥。复杂度最高但最灵活,是第 10 章会详细讨论的方案。
| 设计项 | 要求 | 不满足会怎样 |
|---|---|---|
| 确定性 | 相同输入必须得到相同密钥 | 文件无法解密,等于数据丢失 |
| 可绑定 | 派生输入包含文件唯一标识 | 密钥之间相关,一处泄露波及全部 |
| 单向性 | 无法从派生结果反推主密钥 | 拿到任一文件密钥即可反推主密钥 |
| 用途隔离 | 不同用途从不同主密钥派生 | 一个用途出问题波及全部用途 |
| 算法可替换 | 派生算法可随主密钥版本升级 | 算法有弱点时无法补救 |
4.4 删除文件即删除密钥
这一节是密码学擦除唯一的兑现动作。前面讲的所有机制,最终都要汇聚到这一件事上:删除文件时,销毁它的文件密钥。
完整动作有四步,顺序不能颠倒:
- 确认销毁目标——按文件标识定位到文件密钥的存放位置,确认这是唯一一份副本。注意:如果密钥表里有备份条目、或者文件系统做了快照,这里就漏了。
- 标记密钥失效——把密钥条目标记为不可用,让后续任何读取尝试都失败。这一步保证「销毁过程中崩溃」也不会导致旧密钥继续可用。
- 物理擦除密钥记录——把密钥记录所在的位置擦除或覆写。这一步是为了减少密钥在介质上的残留量,属于纵深防御,不是主要保证。
- 向存储层发出块失效通知——通知介质对应的数据块可以被回收,减少数据本体的残留。
注意这四步里没有一步是在删除数据。数据始终留在原地,保持密文状态。这正是密码学擦除与覆盖写入在成本上的根本差异,也是它能用于大体量数据的前提。
关于「重复文件名」的处理也必须明确:文件密钥如果绑定的是文件名而不是文件唯一标识,那么删除 A 再新建一个同名 A,新文件会继承旧文件的密钥,从而能解开旧文件残留的密文。这是一个真实存在的实现缺陷,设计时必须用唯一标识而不是名字来绑定。
这个缺陷的成因很自然:实现时图方便,用文件名做绑定键。但它的后果是:攻击者只要保留旧文件的一份密文副本,等同名文件被重新创建后,就能读出旧内容——而此时系统日志里显示「旧文件已删除」,一切看起来都正常。
正确做法是用文件的唯一标识(常见做法是内容无关的唯一 ID,而非路径或名字)作为派生输入的一部分,并保证该标识在删除后不会被复用。这个细节在文件系统层实现时容易被忽略,因为从文件系统的角度看,两个同名文件在不同的时刻确实是「不同的文件」。
| 步骤 | 动作 | 失败时的后果 | 可重试性 |
|---|---|---|---|
| 1 定位 | 按唯一标识定位密钥记录 | 找不到即无法确认销毁 | 可重试 |
| 2 标记失效 | 标记为不可用 | 崩溃后旧密钥可能仍被读到 | 可重试且幂等 |
| 3 物理擦除 | 擦除密钥记录位置 | 密钥在介质上仍有残留 | 可重试 |
| 4 块失效通知 | 通知介质回收数据块 | 数据本体残留量偏大 | 可重试 |
4.5 快照与备份的遗留风险
快照是密码学擦除最常见的漏洞来源。原因很简单:快照记录的是某一时刻的完整数据视图,而密钥表通常也在这份视图里。删除文件后,主数据区的数据被标记为无效,密钥记录被擦除——但如果快照是在这之前拍的,快照里的那份密钥记录仍然完好。
更麻烦的是,快照本身通常没有「让密钥失效」的手段。存储系统不会因为你删除了一个文件,就自动去遍历所有快照并同步删除对应的密钥条目。于是就出现了一个矛盾的状态:业务逻辑上文件已经彻底删除,物理上却存在一份可以直接解密的完整副本。
可行的应对方式有四种,按彻底程度递增:
- 缩短快照保留期——让快照在删除动作之后很快过期。简单,但受存储系统的最短保留期约束。
- 删除时同步处理快照——在删除流程里显式触发快照链的清理。彻底,但操作代价高,且不同存储系统的接口差异很大。
- 把密钥表排除在快照之外——让快照只包含数据本体,密钥单独存放与备份。这是最干净的做法,但要求密钥存储与数据存储分离。
- 为每个快照周期派生独立密钥——快照与数据用不同的密钥,使得某个快照的密钥被销毁后,该快照内的密文全部失效。代价是密钥管理复杂度大幅上升。
| 风险来源 | 为什么会残留 | 应对方式 | 彻底程度 |
|---|---|---|---|
| 存储快照 | 快照保留了删除前的密钥表 | 缩短保留期 / 同步清理 / 排除密钥表 | 取决于实现 |
| 数据备份 | 备份含密钥表副本 | 备份加密并独立管理密钥 | 中等 |
| 文件系统自身副本 | 日志与元数据可能含密钥 | 启用元数据加密 | 中等 |
| 主机内存缓存 | 密钥在内存中有临时副本 | 及时清零 + 缩短驻留 | 较弱 |
| 导出与转储 | 诊断数据被导出到外部 | 流程管控 + 字段白名单 | 取决于执行 |
| 芯片之外的生产副本 | 产线留存的镜像文件 | 产线密钥管理 | 较弱 |
4.6 密码学擦除的失效场景
把前面几节的失效模式汇总成一张清单,是为了在设计评审时能逐条对照。这些场景按「隐蔽程度」排列,越往后越难在测试中发现。
最隐蔽的一类是密钥有副本。系统日志显示「密钥已销毁」,测试用例通过,但副本存在于另一个备份池、一份导出文件、或一个测试设备里。这一类问题只有在真正做介质级取证时才暴露。
第二类是机制从未真正启用。比如受保护区域在出厂时状态就是错的,或者派生功能因为某个配置项没开而一直走的是明文分支。这一类的特征是:功能看起来正常,数据也确实加密了,但保护强度是零。
第三类是边界之外:数据被导出、被打印到日志、被包含在崩溃转储里。这一类完全不在介质擦除的能力范围内,只能靠流程与输出管控。
| 失效场景 | 外部表现 | 内部真实状态 | 发现难度 |
|---|---|---|---|
| 密钥只是标记删除 | 删除流程正常返回 | 密钥本体仍在介质上 | 高 |
| 密钥存在备份副本 | 一切正常 | 备份池里有一份可用密钥 | 高 |
| 派生走明文分支 | 数据看起来是密文 | 实际未加密或用固定密钥 | 很高 |
| 保护位未正确置位 | 读取受保护区被拒绝 | 区域其实一直可读 | 高 |
| 数据被导出到外部 | 介质上数据已删除 | 外部文件里还有完整副本 | 中 |
| 崩溃转储泄露 | 介质已清理 | 转储文件里有内存明文 | 中 |
| 派生输入绑定错 | 读写都正常 | 同名文件共享密钥 | 很高 |
| 清理失败静默忽略 | 流程返回成功 | 部分密钥未被销毁 | 中 |
这张表的最后一列「发现难度」值得特别强调:最难发现的那几类,共同特点是外部行为完全正常。功能测试通过、回归测试通过、性能达标,但安全强度为零。这解释了为什么安全擦除的有效性不能靠功能测试来保证——它需要专门的介质级验证手段,这部分内容见第 12 章。