Archipelago

Lec 22安全(Security)

讲义完成更新于 2026-09-30

Lec 22 安全(Security)

MIT 6.5900 Fall 2024 · Joel Emer 主题:侧信道(side channel)、信息泄露、攻击的通信模型、缓存(Cache)信道、推测执行攻击(Meltdown / Spectre)

TL;DR

  • ISA 抽象的时序盲区:经典体系结构契约(ISA)仅定义架构状态的逻辑转换,忽略物理实现中的时间延迟与微架构驻留状态,使攻击者能以微架构事件为信道穿透安全隔离域。
  • 侧信道通信模型:将微架构状态(如缓存命中与替换)视为共享物理媒介;攻击者通过预置信道(Prime/Flush)、诱导发送方依秘密数据调制信道、并通过高精度时钟(rdtsc)解调出私密数据。
  • 推测与瞬态执行攻击:Meltdown 利用异常提交延迟推测读取特权内存,Spectre 利用分支预测器训练在短暂推测窗口内执行跨边界越界访存并经由缓存侧信道外发,颠覆了硬件推测安全假定。

一、安全与信息泄露的根源

  • 像虚拟内存这样的硬件隔离机制保证了体系结构状态不会被其他进程直接看到……但是;
  • ISA 是一个与时序无关(timing-independent)的接口:它规定"应该发生什么",而不规定"何时发生";
  • ISA 只规定体系结构状态更新(寄存器、内存、PC……),微架构变化未被规定;
  • 因此,实现细节与时序行为(如微架构状态、功耗等)被用来突破安全机制;
  • 具体而言,它们被当作信道(channel)来泄露信息。

二、攻击的通信模型(Communication Model of Attacks)

标准通信模型

  1. 发送方拿到一条消息;
  2. 发送方调制(modulate)信道;
  3. 接收方在信道上检测到调制;
  4. 接收方把调制解码为消息。

攻击模型的术语 [Belay, Devadas, Emer]

  • 域(Domain):体系结构状态不共享的不同体系结构域(如受害者域、攻击者域);
  • 秘密(Secret):在信道上传输、被接收方检测的"消息";
  • 信道(Channel):某种可被"发送方"改变(调制)、且其调制可被"接收方"检测到的"状态"。

因为该信道不是"直接"通信信道,故常称为"侧信道"(side channel)。

攻击四步:

  1. 发送方访问秘密;
  2. 发送方基于秘密用消息调制信道(微架构状态);
  3. 接收方在信道上检测调制;
  4. 接收方把调制解码为含秘密的消息。

例:ATM 声学信道。秘密 = PIN;发送方 = 键盘;信道 = 空气;调制 = 声波;接收方 = 廉价麦克风;解码器 = 机器学习模型。


三、信道类型

类型调制载体攻击者要求
物理信道(Physical)功耗、电磁、声音……需测量设备、物理接触
时序信道(Timing)处理器响应时间可远程(如经网络连接)
微架构信道(Microarchitectural)微架构事件(如时序、性能计数器)可远程或同处一地

这些信道能做什么

  • 违反特权边界:跨进程通信、推断应用秘密;
  • (半侵入式)应用画像(profiling)。

与传统软件/物理攻击不同:

  • 隐蔽:需要复杂机制才能检测信道;
  • 通常没有被利用过的永久痕迹。

四、时序信道示例

def check(input):
    size = len(passwd)        # passwd 含 8 位数字
    for i in range(0, size):
        if input[i] != password[i]:
            return "error"
    return "success"

执行时间取决于输入与正确密码匹配了多少个字符。攻击者可逐字符暴力破解,最多尝试 10×8=8010 \times 8 = 80 次;而盲猜需最多 10810^8 次。


五、历史与"主动接收方"

1977 年的侧信道

源于磁盘臂优化:请求按柱面号升序入队,按"电梯算法"出队执行。

示例:接收方先请求柱面 55 并暂停;发送方请求 53 或 57;接收方再同时请求 52 与 58。若接收方先收到 52 的数据,就能推断发送方此前请求了哪个柱面。 若去掉第一步(接收方对 55 的请求),攻击不再成立——说明需要一个"主动接收方"来预置(precondition)信道。

主动接收方(Active Receiver)

  • 主动接收方可能需要预置信道以便检测调制;
  • 还需处理预置、传输(调制)、接收(解调)三者之间的同步问题。

六、基于缓存的信道

简单缓存信道

基于缓存的侧信道攻击利用了共享缓存(如末级共享缓存 LLC)的状态竞争机制。攻击者(接收方)与受害者(发送方)即使运行在完全隔离的用户空间或容器中,只要映射到相同的缓存组(Cache Set),就能通过对该组缓存行的驱逐行为传递私密信息。

整个通信协议包含预置、调制与解调三个阶段:

进程 1(发送方)          进程 2(接收方)
write to set            if (send '0')
                            idle
                        else
                            write to a set
                        t1 = rdtsc()
                        read from the set
                        t2 = rdtsc()
                        if t2 - t1 > hit_time: decode '1'
                        else                   decode '0'

信道调制与解调原理解析:

  • 信道预置(Prime):接收方先通过连续访问一系列冲突地址,将目标组全部填满自己的私有缓存行,使其处于已知的缓存命中状态。

  • 发送方调制(Modulation):发送方根据自身持有的私密数据比特决定是否触碰该组。若秘密比特为 1,则访问对应地址并导致该组发生冲突替换,将接收方的缓存行驱逐至主存;若秘密为 0,则保持静止。这种依秘密值改变微架构驻留状态的操作,在数学上类似于无线电中的调幅(AM,Amplitude Modulation):

    secret = oneof(0..1)
    if secret == 1:
        x = channel    # 发生信道调制
  • 接收方解调(Probe):接收方使用高精度时间戳计数器(如 x86 的 rdtsc 指令)测量重新访问自己先前预置数据的耗时。若时延 t2−t1t_2 - t_1 远大于典型 L1/L2 命中阈值(即发生了缓存缺失 Cache Miss 并访问 DRAM),说明发送方曾写入该组,从而成功解码出比特 1;反之若依然命中,则解码出比特 0。

RSA 中的"AM 发送方" [Percival 2005]

平方-乘(square-and-multiply)幂运算中存在依赖秘密的内存访问,从而成为发送方:

r = 1
for i = n-1 down to 0 do
    r = sqrt(r); r = mod(r, m)
    if e_i == 1 then              # 依赖秘密的分支
        r = mul(r, b); r = mod(r, m)
end
return r

多路组相联缓存信道:Prime + Probe

  • 预置(Prime):填满一个 set;
  • 传输:发送方按秘密决定是否写该 set;
  • 接收(Probe):读整个 set 并计时;
  • 解码:超过命中时间则解码 '1',否则 '0'。

推广到其他资源

任何"被使用后状态改变、并可被计时检测"的硬件资源都可作信道:

资源共享范围
私有缓存(L1、L2)核内
共享缓存(LLC)同 socket 跨核
缓存目录跨 socket
DRAM 行缓冲跨 socket
TLB(私有/共享)核内/核间
分支预测器核内
片上网络(NoC)同 socket 跨核

七、信道噪声

  1. 另一个(或同一个)发送方可能向信道引入状态变化(噪声),混淆接收方(接收方可能把噪声当信号);
  2. 接收因此变成概率性的,需要对接收方做随机分析来解码;
  3. 可通过改进消息编码(如重复发送)提升接收可靠性。

多子信道发送:subchannel[secret] = 1,秘密取值 0..3,噪声使各子信道的信号呈概率分布。


八、缓解手段(Mitigations)

1. 不使用信道:常时编程

与秘密无关的信道调制(Secret-independent modulation)——即"数据无关执行"(data-oblivious execution)或"常时编程"(constant-time programming)。

把 RSA 中依赖秘密的分支改为无分支的条件移动:

p = (e_i == 1)
r2 = mul(r, b)
r2 = mod(r, m)
cmov [p] r, r2          # 无依赖秘密的分支/访存/浮点操作

常时编程很难:去掉依赖秘密的分支后,还要追问这些函数内部是否仍有泄露。

2. 扰乱时钟

"我们发现,找出系统中所有精确时钟,比找出所有可能的时序信道要容易得多。……若让时钟不那么精确,系统中所有时序信道的有效带宽都会降低。"(1991)

3. 不相交信道(Disjoint Channels)

  • 使信道不相交可让通信变得不可能;
  • 信道可按"域"分配,并在进程进入/离开运行态时清理,使下一个进程看不到任何调制(如 DAWG,Kirianski et al., Micro'18)。

4. 混淆信道(Obfuscation)

  • 加单个哈希:使接收方难以构造监视特定 set 的地址(各进程地址不再一一对应),但接收方仍可逆向推出会被调制的子信道;
  • 加进程相关哈希:使所需的缓存冲突变成概率性,接收方需额外步骤去探测多种"信道";
  • 接收方校准(Calibration):校准单元确定接收方需用哪些子信道来检测调制——可观察已知传输,或主动激发发送方做特定传输。

九、发送方的类型

  1. 预先存在:受害者自身泄露秘密(如 RSA 密钥);
  2. 由攻击者编写并调用(如 Meltdown);
  3. 从受害者已有代码合成、由攻击者调用(如 Spectre v2)。

十、推测执行攻击

提醒:推测执行(Speculative Execution)

在 x86 中,页表可含仅内核态可访问的内核页(避免上下文切换时换页表)。硬件推测性地假设不会发生非法访问,于是非法指令之后的指令被推测执行。

val = *kernel_address;   // 用户态执行:触发保护故障,
                         // 但 kernel_address 的数据被推测性读入 val

Meltdown [Lipp et al. 2018]

  1. 预置:接收方分配数组 subchannels[256] 并刷出所有缓存行;
  2. 传输:攻击者控制的发送方执行:
uint8_t secret = *kernel_address;
subchannels[secret] = 1;
  1. 接收:处理完保护故障后,接收方对 subchannels[256] 全部计时,找出被"调制"的子信道,解出秘密。

结果:攻击者可读取任意内核数据!为提高性能可用事务内存(异常时事务中止而非进内核)。 缓解:内核页表隔离(KPTI,不把内核数据映射进用户页表);或在权限检查失败时返回零(支持精确异常)。

Spectre variant 1 [Kocher et al. 2018]

利用条件分支误预测,让内核推测执行受分支保护的"发送方"代码:

if (x < array1_size)
    y = array2[array1[x] * 4096];
  1. 预置:刷出 array2 全部元素;
  2. 训练:用小 x 反复调用,把分支预测器训练成"taken";
  3. 传输:用越界 x 调用,使 &array1[x] 指向目标内核地址;核误预测分支,推测性地把 &array2[array1[x]*4096] 取入缓存;
  4. 接收:探测缓存,推断 array2 哪一行被取入,得知内核地址的数据。

Spectre variant 2 [Kocher et al. 2018]

利用间接分支预测器(多数 BTB 只存源地址部分 tag):

  1. 训练:多次触发 victim_branch → xmit;
  2. 传输:让 victim_branch 与 training_branch 在 BTB 中别名,从而推测性触发 victim_branch → xmit;
  3. 接收:与 Spectre v1 类似。

Spectre 变体与缓解

  • Spectre 依赖推测执行,而非延迟的异常处理 → 比 Meltdown 更难修复;
  • 还有多种变体:利用推测存储缓冲、返回地址栈、泄露特权寄存器等;
  • 可攻击任意类型的 VM,包括 OS、VMM、浏览器中的 JavaScript 引擎、以及 OS 网络栈(NetSpectre);
  • 短期缓解:微码更新(尽量禁止共享推测状态)、OS 与编译器补丁(选择性避免推测);
  • 长期缓解:禁用推测?关闭侧信道?

硬件安全与微架构侧信道总结

  • ISA 是与时序无关的接口,只规定"做什么"而不规定"何时做",也不规定微架构状态;
  • 实现细节(如推测执行)与时序行为(微架构状态、功耗)被用来突破安全机制;
  • 作为软硬件契约的 ISA,不足以对微架构安全进行推理。

ISA 契约失效:推测执行时代的微架构状态困境

数十年来,计算机体系结构遵循一个经典契约:无论乱序执行或推测分支在流水线底层如何激进调度,只要在提交(Commit)时精确按序提交架构状态(Architectural State),错误路径上的推测指令被废弃(Squash)后就如同从未发生过一样。然而 Meltdown 与 Spectre 无情击碎了这一假定:架构状态回滚了,微架构状态(Microarchitectural State,如 Cache 命中状态、TLB 项、分支预测器饱和计数器)却留在了物理芯片上。推测执行实质上赋予了非特权代码一种能力——在硬件最终判定非法前的短暂「瞬态窗口(Transient Window)」内,以推测方式读取越权秘密并将其调制为微架构状态变更。要彻底根治此类侧信道,不仅要在软件层做隔离,更需要重新审视软硬件分界线,将时序可观测性(Observability)作为一等公民纳入硬件规格契约。

相关后续课程:Secure Hardware Design 6.5950/1(旧号 6.S983 / 6.888,https://shd.mit.edu )


注:本讲涉及计算机安全攻击与防御,属于敏感主题,整理目的为体系结构学习。若用于实际系统安全评估,请遵循相关法律与道德规范。