Lec 18特权分离(Privilege Separation)
Lec 18 特权分离(Privilege Separation)
MIT 6.1600 · Introduction to Computer Security
TL;DR
- 特权分离从“组件终会被攻破”出发,把系统拆成仅持最小权限的进程或服务,使单点失陷不会直接获得全部能力。
- 边界应隔离高风险解析器、长期密钥和高价值数据;跨边界接口必须同时足够完成任务、足够窄以限制被攻破调用方。
- OpenSSH、浏览器和支付系统说明分离可以显著缩小损害范围,但更多组件与 RPC 也增加状态同步、性能和接口漏洞成本。
1. 核心思想
软件必然存在 bug,单纯消除 bug 难以实现。另一策略:
规划妥协(Plan for Compromise)——假设某个组件会被攻破,通过架构设计限制损害范围。
最小权限原则(Principle of Least Privilege):
每个组件只应拥有完成其工作所必需的最小权限集合。
该原则是难以完全达到的设计目标,而非硬性规则——在安全性与复杂度之间权衡。
2. 实施挑战
2.1 边界划分(Splitting Boundaries)
将大型软件划分为组件时,需找到合适的边界:
| 划分方式 | 示例 |
|---|---|
| 按用户隔离 | OS 中不同用户的账号 |
| 按功能分割 | 大型业务系统中搜索/用户/邮件作为独立服务 |
| 隔离高风险代码 | Firefox 将视频 Codec 放入沙箱 |
| 前端与核心隔离 | Google 前端 HTTP 服务器与内部数据库服务器分离 |
| 密钥隔离 | CA 用 HSM 隔离签名密钥与其他代码 |
2.2 接口设计(Interface/API)
组件间交互 API 必须:
- 功能完整:允许合法操作
- 安全限制:防止组件 A 被攻破后危害组件 B
- 低性能开销:隔离不能使系统无法使用
- 低复杂度:接口代码本身不引入新漏洞
接口实现方式:RPC(远程过程调用)、消息队列、共享内存、共享数据库、共享文件。
3. 实例:日志系统
目标:攻击者攻破应用服务器后无法删除审计日志。
架构:
应用服务器 ---[仅追加]---> 日志服务器 ----> 日志存储
(Append/Read API, 无 Delete)
- 应用服务器只能追加日志,不能删除
- 即使应用服务器被攻破,攻击者仍无法抹除历史日志
4. 实例:密钥管理
目标:即使应用被攻破,攻击者无法提取签名私钥。
架构:
应用 ---[sign(msg) / get_public_key()]--> 密钥管理器(含 sk)
(没有提取 sk 的 API)
攻击者即使控制了应用,也只能调用 sign(),不能提取 sk。
可以在密钥管理器中增加策略:只签名符合格式的消息,限制攻击者伪造的范围。
同时密钥管理器记录所有签名操作日志,为事后取证提供证据。
5. 实例:Firefox 媒体 Codec 沙箱
JPEG 等媒体 Codec 历史上漏洞频发,是浏览器攻击的常见入口。
架构(WebAssembly 语言级隔离):
浏览器 Tab ---[编码数据]--> Codec 沙箱
<---[位图]----
- Codec 在独立的 WebAssembly 沙箱中运行
- 若恶意图片触发 Codec 漏洞,攻击者只能访问 Codec 进程的内存,无法直接访问用户 Cookie 等数据
API 设计挑战:视频解码是增量的(Progressive)——接口设计需同时支持流式解码和低延迟,避免引入新漏洞。
6. 实例:NTP 客户端(操作系统时间服务)
设置系统时间需要 root 权限,但 NTP 代码需要复杂的网络处理(易有 bug)。
架构:
网络 <---> NTP 网络进程(无 root)
|
[adjust clock]
↓
时间服务(有 root,执行策略)
- 网络代码 bug 只允许攻击者影响时间,不能执行任意 root 操作
- 时间服务可加策略(如时间不能倒退)
7. 实例:OpenSSH 服务器
SSH 服务器需要访问:网络端口 22、主机签名密钥、密码数据库、用户文件。
架构:
[ 主机密钥 ] [ 密码 DB ] [ 端口 22 ]
|
↓
Monitor 进程(持有所有权限)
|
├── 每个连接生成 Worker 进程
| (无密钥、无密码 DB 访问)
| ↕ 通过有限 API 与 Monitor 通信
|
└── 认证成功后创建 Session 进程
Worker 与 Monitor 的 API:
sign(transcript)— 请求 Monitor 用主机密钥签名check_password(user, pw)— 密码验证(Monitor 可限速)start_session(user)— 创建 Shell 进程
网络层漏洞最多让攻击者控制 Worker,不能直接获取主机密钥或密码数据库。
8. 实例:Web 服务架构
Client --TLS--> [前端 HTTPS 服务器,持有 TLS 密钥]
|
+---> [Login 服务] <--> [密码 DB]
|
+---> [Profile 服务] <--> [用户 DB]
|
+---> [Photo 服务] <--> [Photo DB]
- 各后端服务只有各自 DB 的访问权
- 前端服务器隔离 TLS 密钥与业务逻辑
- Login 服务被攻破 → 仅影响密码 DB,不影响 Photo DB
9. 实例:浏览器与 Web 客户端
Gmail PDF 附件:通过 googleusercontent.com 域名提供,与 google.com 不同 Origin:
- Same-Origin Policy 防止
googleusercontent.com的代码读取google.com的 Cookie - 即使 PDF 触发漏洞,攻击者代码无法直接偷取 Google 账号 Cookie
浏览器多进程架构:
Codec 沙箱 <--> mit.edu 进程
Codec 沙箱 <--> nytimes.com 进程
|
浏览器核心 <--> UI <--> GPU
|
[Cookies, 缓存]
完全攻破浏览器需要同时利用多个组件的漏洞。
10. 实例:支付系统
在线商户将信用卡数据完全外包给支付处理商:
Client --[商品选择]--> 商户网站
|
↓(重定向到支付页)
Client --[信用卡数据]--> 支付处理商 ---> Visa/MC
|
↓(支付完成,重定向回商户)
商户收到支付确认,但从不接触信用卡数据
商户系统被攻破也无法获取信用卡信息——数据从未到达商户服务器。
关键原则总结
| 设计决策 | 安全收益 |
|---|---|
| 功能按最小权限拆分 | 单组件失陷不影响整体 |
| 窄且严格的组件间 API | 减少跨组件攻击面 |
| 密钥/凭据单独隔离 | 防止应用层攻击窃取凭据 |
| 只追加日志 | 防止攻击者销毁证据 |
| 前端/后端隔离 | 暴露在互联网的代码攻破面最小化 |
我的理解
最小权限只有与最小接口同时实施才有效;拆分进程却保留宽泛 IPC,仍会把高权限能力暴露给低信任代码。