Archipelago

Lec 13隔离(Isolation)

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

Lec 13 隔离(Isolation)

MIT 6.1600 · Introduction to Computer Security

TL;DR

  • 隔离要求一个域的行为不能非法影响或泄露给另一个域;非干扰比“没有直接读写”更强,因为它还排除可观察的间接影响。
  • 模拟执行、时间复用、地址翻译与软件插桩从不同层面建立边界,虚拟机和浏览器沙箱通常组合多种机制。
  • 共享缓存、调度与推测执行仍会形成侧信道,说明功能隔离不等于信息流隔离;缓解需要分区、刷新、随机化或减少共享。

1. 隔离的动机

在云计算、移动平台、浏览器中,多个互不信任的程序共享同一硬件:

  • AWS EC2:同一物理机运行多个租户的 VM
  • 手机:多个 App 共享同一 CPU 和内存
  • 浏览器:多个标签页(origin)并存

当一个组件出现恶意行为或 bug 时,系统需保证其无法影响其他组件——这就是隔离域(Isolation Domain)。

2. 形式化定义

2.1 弱完整性

对手域 A 不能修改受害者域 V 的状态 SVS_V:

(SA,SV)→run A(SA′, SV)(S_A, S_V) \xrightarrow{\text{run}\ A} (S'_A,\ \mathbf{S_V})

2.2 弱机密性(Non-Leakage)

A 的输出对 V 的状态不敏感:

∀SV1,SV2: (SA,SV1)→run A(SA′,−) = (SA,SV2)→run A(SA′,−)\forall S^1_V, S^2_V:\ (S_A, S^1_V) \xrightarrow{\text{run}\ A} (S'_A, -)\ =\ (S_A, S^2_V) \xrightarrow{\text{run}\ A} (S'_A, -)

2.3 非干扰(Non-Interference,强性质)

并发运行时上述性质仍成立:

run(A,V)≡run(A only), 从 A 的视角\text{run}(A, V) \equiv \text{run}(A \text{ only}),\ \text{从 A 的视角}

2.4 为什么非干扰难以实现

只要两个域共享任何资源(CPU 时间、内存容量、磁盘带宽),就存在侧信道(Side Channel):

内存侧信道:

受害者: malloc(secret)   # 分配与 secret 成比例的内存
攻击者: 不断 malloc 直到失败 → 推断 secret

时序侧信道:

受害者: 执行时间取决于 secret(如 RSA 幂运算)
攻击者: 测量时间 → 推断 secret

实践:大多数隔离机制放弃严格非干扰,选择"好用"的近似隔离。

3. 实现隔离的机制

隔离实现需回答三个问题:

  1. 每个域的**状态(State)**是什么?
  2. 哪些操作会访问状态?
  3. 如何强制保证操作只影响本域状态?

3.1 模拟执行(Emulation)

解释器逐指令解析并在隔离状态上执行,每条指令前检查权限:

  • JavaScript 引擎、Python 解释器
  • 优点:实现简单,隔离强
  • 缺点:速度慢(每条逻辑指令需多条物理指令);无法利用专用硬件

3.2 时间多路复用(Time Multiplexing)

每次只让一个域运行,切换时:

  1. 保存当前域的 CPU 寄存器、内存
  2. 清零硬件状态
  3. 加载下一个域的状态
  • 游戏机的进程切换
  • 优点:域内程序可完全控制硬件
  • 缺点:上下文切换代价高(需保存/恢复大量状态)

3.3 命名/翻译(Naming / Translation)

隔离程序不能直接访问物理资源——只能通过由隔离机制控制的虚拟指针:

虚拟地址→页表(由 OS 控制)物理地址\text{虚拟地址} \xrightarrow{\text{页表(由 OS 控制)}} \text{物理地址}

虚拟内存:每个进程的虚拟地址空间映射到独立的物理页,不同进程的虚拟地址指向不同物理内存。

其他例子:

  • 文件描述符(Linux):进程只能操作被 OS 分配的文件,不能任意打开
  • 虚拟 VLAN(网络):不同虚拟网络共享物理交换机但相互隔离

3.4 软件插桩(Software Interposition)

编译器自动在每次内存访问前插入边界检查:

// 原始代码
var a = b[c];

// 插桩后
if (c >= b.size) error();
load b+c -> a;

程序直接在硬件上运行(无模拟),但代码内部自动执行检查。

挑战:函数指针的处理——若攻击者能控制函数指针的值,可绕过检查跳过安全代码。

4. 案例:虚拟机隔离

状态:寄存器值、内存内容、磁盘数据
操作:CPU 指令(寄存器/内存)、设备 I/O

操作类型隔离机制
寄存器更新时间多路复用(每次一个 VM)
内存访问命名/翻译(虚拟内存,VMM 控制页表)
设备 I/O模拟执行(VM 的 I/O 指令陷入 VMM)

KVM / VMware 使用硬件虚拟化(Intel VT-x / AMD-V)加速切换,减少 VMM 介入。

5. 侧信道攻击(Side-Channel Attacks)

即使完整性和机密性成立,侧信道仍可泄露信息。

5.1 缓存时序侧信道

CPU 缓存(Cache)使内存访问时间不均等:

  • 缓存命中:~4 个时钟周期
  • 缓存缺失:~200 个时钟周期

若受害者的内存访问模式取决于 secret,攻击者通过测量时间可推断 secret。

5.2 Flush+Reload 攻击

  1. 攻击者 flush 某共享内存行(使其驱逐出缓存)
  2. 受害者运行(可能访问该内存行)
  3. 攻击者 reload 该内存行,测量时间
    • 时间短 → 受害者访问了该行 → 推断 secret 的某一位

5.3 Spectre / Meltdown(硬件级侧信道)

利用 CPU 的**推测执行(Speculative Execution)**机制:

  • CPU 为了加速会"预测"分支结果并提前执行
  • 若预测错误则回退,但缓存状态已被修改
  • 攻击者通过缓存侧信道读出推测执行阶段的内存内容

推测执行泄露+缓存侧信道⇒跨 VM/跨进程 内存读取\text{推测执行泄露} + \text{缓存侧信道} \Rightarrow \text{跨 VM/跨进程 内存读取}

Meltdown(CVE-2017-5754):利用内核页面权限检查延迟,用户进程可读内核内存。
Spectre(CVE-2017-5753/5715):利用分支预测器,可读同进程内其他隔离沙箱的内存。

5.4 缓解措施

攻击缓解措施
MeltdownKPTI(内核页表隔离):用户态不映射内核地址
SpectreRetpoline(返回地址蹦床);浏览器降低定时器精度
Cache 侧信道常数时间算法(Constant-time programming)

常数时间编程原则:密码学代码中访问的内存地址和执行时间不依赖秘密数据:

// 错误(时序依赖密钥位)
if (key_bit) access(table[i]);

// 正确(无分支,无条件加载两个值)
uint8_t v0 = table[0][i], v1 = table[1][i];
result = select(key_bit, v0, v1);  // 用 bit masking 实现

关键概念总结

概念说明
隔离域相互隔离的执行单元
非干扰A 运行后看不到 V 的任何信息(强理想性质)
侧信道通过共享资源的观察(时间/缓存)泄露信息
Spectre推测执行 + 缓存侧信道,突破语言级沙箱
Meltdown推测执行突破内核/用户态隔离
KPTIMeltdown 的 OS 级缓解:用户态不映射内核页

我的理解

隔离与资源共享天然冲突;越追求缓存、内存和硬件的复用效率,越需要明确可观察的共享状态。