本节摘要:内存隔离是 TEE 最基础的安全机制,它在共享的物理内存里为可信应用划出一块"不可见、不可改、不可推测"的私有区域。本节讲清硬件怎么做这个隔离——TrustZone 靠安全状态位、SGX 靠 EPC 加密加 MAC、SEV 靠 VM 级透明加密;以及为什么侧信道攻击是这套机制绕不开的软肋,恒定时间编码和缓存分区怎么缓解。
阅读完本节,你应当能够:
TEE 的核心承诺是"敏感数据在内存里对外不可见"。这个承诺听起来直白,做起来极难。要知道,物理内存(DRAM)是共享的——操作系统、Hypervisor、甚至其他虚拟机都可能通过某种途径访问到同一块物理内存。如果攻击者拿到了内核权限,他能直接 dump 内存;如果他能物理接触机器,他能用探针读 DRAM 芯片;就算都做不到,CPU 微架构的共享资源(缓存、分支预测器)也可能成为信息泄露的旁路。
所以"内存隔离"不是一道墙那么简单,它是一套多层次的防御体系。最外层是访问控制(挡住直接读),中间是加密(挡住物理探针和内存 dump),内层是完整性校验(挡住篡改和重放),还得应对最隐蔽的侧信道(微架构状态泄露)。每一层都补一种攻击,少了哪一层都有对应的突破口。
理解这套体系怎么分层、每层挡什么,你才能判断自己系统的内存安全水位到底在哪里。
不同 TEE 架构实现内存隔离的方式差别很大,但目标一致:让非授权实体无法读取或修改 TEE 内存。
ARM TrustZone:安全状态位 + 总线仲裁。TrustZone 不加密内存,而是靠"访问控制"隔离。每个内存页表项带一个安全属性位,CPU 处于非安全状态时,即便构造出指向安全内存的地址,内存控制器也会拒绝访问——因为总线上的"非安全"信号和目标区域的"安全"标记对不上。这种隔离是物理级的(靠硬件总线信号),内核改不了页表也绕不过。
Intel SGX:EPC 加密 + MAC 完整性校验。SGX 把飞地内存放在 EPC 里,写入前用硬件密钥加密,读取时自动解密。除了加密,每一页还关联一个 MAC(消息认证码),由页内容、页号和 CPU 密钥生成。任何非法修改都会让 MAC 对不上,触发异常。加密挡读取,MAC 挡篡改。
AMD SEV:VM 级透明加密。SEV 给每个虚拟机分配一个唯一的 AES 密钥,整个 VM 的内存写入 DRAM 前加密、读取时解密。对 VM 内部的操作系统和应用完全透明(它们感觉不到加密)。Hypervisor 虽然调度 VM,但 dump 内存看到的也是密文。
| 架构 | 隔离机制 | 加密 | 完整性 | 粒度 |
|---|---|---|---|---|
| TrustZone | 安全状态位+总线仲裁 | 无 | 无 | 系统级 |
| SGX | EPC 加密+MAC | 有 | 有 | 应用级 |
| SEV-SNP | VM 内存加密+RMP | 有 | 有 | 虚拟机级 |
注意 TrustZone 不加密内存——它靠访问控制挡住非安全世界的直接读取。这意味着如果攻击者能物理接触 DRAM(用探针),TrustZone 挡不住——这正是它把物理攻击排除在威胁模型外的技术原因。SGX 和 SEV 因为有内存加密,连物理探针都只能看到密文。
很多人以为"内存加密了就安全了",这是只看了机密性、漏了完整性。只加密不做完整性校验,会被重放攻击绕过。
重放攻击的原理:攻击者把 TEE 内存里某个密文页的内容记下来(他读不到明文,但密文他能 dump)。过一会儿,趁 TEE 没注意,他把这个旧版本覆盖回去。如果系统只检查"内容能不能解密"而不检查"是不是最新的",TEE 就会用旧状态继续跑——可能把已经撤销的权限又激活了,或者把已经花掉的钱退回来。
所以完整的内存保护必须同时做加密和完整性校验。SGX 的 MAC 不光校验内容对不对,还隐含版本信息(通过完整性树维护)。SEV-SNP 的反向页表(RMP)则保证每个物理页归属正确、未被非法重映射。三者(加密、MAC、RMP)协同才能堵住读取、篡改、重放三类攻击。
💡 关键直觉:机密性(加密)和完整性(校验)是两条不同的防线,必须同时存在。历史上 SGX 早期版本完整性保护不够强,被重放类攻击搞得焦头烂肿,后来 SGX2 和 SEV-SNP 才把这块补齐。这个教训适用于所有 TEE 设计——只加密不校验,等于门锁了但窗户开着。
除了 CPU 访问内存,还有一种常被忽视的途径:DMA(直接内存访问)。很多外设(网卡、显卡、存储控制器)能绕过 CPU 直接读写内存,速度极快。如果恶意外设通过 DMA 读 TEE 内存,CPU 层面的隔离就形同虚设。
防御手段是 IOMMU(I/O 内存管理单元)。它像 CPU 的 MMU 一样,给外设的 DMA 访问做权限控制。TEE 系统会配置 IOMMU,确保外设只能访问被授权的内存区域,无法通过 DMA 探测安全内存。TrustZone 叫这个机制"安全 DMA",SGX 和 SEV 也依赖 IOMMU 挡住外设窥探。
DMA 攻击在历史上真实发生过——某些恶意 Thunderbolt 设备就是利用 DMA 漏洞读取目标机器内存。所以严肃的 TEE 部署一定会启用 IOMMU 并严格配置外设权限。
内存加密和完整性校验做得再好,也防不住侧信道攻击。这是 TEE 安全模型里最棘手的部分。
侧信道的核心原理:虽然攻击者读不到 TEE 内存内容(加密了),但程序执行时会通过共享的微架构资源泄露信息——缓存命中模式、分支预测状态、执行时间、功耗波动都可能成为信息载体。攻击者通过观察这些旁路信号,能间接推断出 TEE 内部的敏感数据。
几个著名的侧信道攻击:
| 攻击名 | 利用什么 | 泄露什么 | 年份 |
|---|---|---|---|
| Foreshadow | L1 数据缓存推测执行 | SGX 飞地内的密钥 | 2018 |
| CacheBleed | 缓存银行冲突 | RSA 私钥 | 2017 |
| Spectre/Meltdown 衍生 | 推测执行+缓存 | 跨飞地内存内容 | 2018 |
| SGAxe | Foreshadow 衍生 | SGX 根密钥 | 2020 |
这些攻击之所以危险,在于它们绕过了传统的内存隔离——隔离没破,但 CPU 微架构的共享资源成了泄露通道。两个飞地(或飞地与宿主)共享同一颗 CPU 的缓存、分支预测器、TLB,只要这些资源的状态能被观察,信息就可能泄露。
⚠️ 常见坑:很多文档把内存加密描述成"铜墙铁壁",给人"放进 TEE 就绝对安全"的错觉。实际上侧信道至今是开放问题,没有任何 TEE 能宣称"完全防住所有侧信道"。把高敏感逻辑放进 TEE 后,还必须在软件层做恒定时间编码、限制缓存共享,才算到位。
既然侧信道防不住,至少要降低泄露。工程上常用的手段:
恒定时间编码(Constant-time Programming)。让敏感运算(加密、比较、条件分支)的执行时间不依赖秘密数据。比如比较两个字节是否相等,不要用"相等就提前返回"的短路逻辑(这会让执行时间泄露"在第几位不相等"),而是一直比较完所有位再返回结果。这样攻击者从执行时间推断不出任何信息。
缓存分区。让 TEE 代码独占一部分缓存,不与不可信代码共享。Intel SGX 后续版本引入了更细粒度的缓存分区,ARM 也提供了缓存隔离的硬件支持。这能削弱基于缓存的侧信道。
数据无关内存访问。让 TEE 代码的内存访问模式不依赖秘密数据。比如查表时,不要根据秘密值决定访问表的哪一行(这会让缓存命中模式泄露秘密),而是用 oblivious 算法让访问模式看起来是随机的。
| 手段 | 防什么 | 代价 |
|---|---|---|
| 恒定时间编码 | 时间侧信道 | 代码复杂、性能略降 |
| 缓存分区 | 缓存侧信道 | 缓存利用率下降 |
| 数据无关访问 | 访问模式侧信道 | 算法复杂、性能损失大 |
不同架构的侧信道风险不同,选型时要考虑:
如果你的业务对侧信道极其敏感(比如密钥运算),考虑上 HSM(硬件安全模块),它专门为抗侧信道设计,但成本高得多。
落地 TEE 后,怎么评估内存安全水位?我一般检查这张清单:
| 检查项 | 满足 | 风险 |
|---|---|---|
| 内存加密启用 | TEE 内存对外是密文 | 否则物理探针可读 |
| 完整性校验启用 | 篡改和重放可检测 | 否则有重放风险 |
| IOMMU 配置正确 | 外设 DMA 被限制 | 否则有 DMA 攻击 |
| 恒定时间编码 | 时间侧信道被削弱 | 否则时间泄露秘密 |
| 缓存隔离 | 缓存侧信道被削弱 | 否则邻居可窥探 |
下一节看 TEE 怎么从上电那一刻就保证可信——可信启动的度量链怎么建立信任起点,远程证明怎么把这个信任外化成远方可验证的证据。
补一笔容易被忽视的性能账。内存加密引擎工作在内存总线上,理论上对软件透明,但天下没有免费的加密:加密页的写放大——加密块的修改需要整块重加密,写密集负载的延迟上升可感知;加密引擎的带宽墙——高带宽场景(内存密集型计算)下引擎吞吐可能成为新瓶颈;密钥切换的缓存代价——多个飞地共存时,跨飞地的内存访问伴随密钥切换与缓存冲刷。实测数量级:一般业务负载性能折损在个位数百分比到两成之间,但内存带宽饥饿型的负载(大数组扫描、数据库全表扫描)可能折损三成以上。工程结论很直接:把最敏感的少量数据放进 TEE,把吃带宽的计算留在飞地外——"飞地是保险柜不是体育馆",用最小的边界保护最值钱的资产,性能与安全就都站得住。