try ai
科普
编辑
分享
反馈
  • 访问矩阵

访问矩阵

SciencePedia玻尔百科
核心要点
  • 访问矩阵是一个抽象模型,它通过将主体(用户、进程)和客体(文件、设备)映射到特定的访问权限来定义系统的保护状态。
  • 它主要通过两种方式实现:以客体为中心的访问控制列表 (ACL) 或以主体为中心的、称为能力 (capability) 的不可伪造令牌。
  • 基于能力的系统通过强制执行最小权限原则,从根本上避免了“困惑的代理人问题”,在这种原则下,程序没有任何环境权限。
  • 该模型的原理是各种应用中安全的基础,包括操作系统内核、虚拟化、物联网设备以及大规模的基于角色的访问控制 (RBAC) 系统。

引言

在复杂的计算世界中,我们如何强制执行秩序并建立信任?安全的基本问题始终是:谁被允许对哪些资源做什么?即使对于一个中等复杂的系统,回答这个问题也可能令人望而生畏,但计算机科学提供了一种优雅而强大的抽象来推理此问题:​​访问矩阵​​。这个简单的网格将活动实体(主体)映射到受保护的资源(客体)以及特定的权限(权利),构成了数字系统中保护的通用蓝图。

尽管这个概念很简单,但真正的挑战在于将这一抽象付诸实践。我们如何高效、安全地实现这个矩阵?不同实现哲学之间的权衡是什么?本文旨在通过全面探讨访问矩阵模型来弥补这一知识空白。首先,在“原理与机制”一章中,我们将剖析该模型的核心组件,比较两种主流实现策略——访问控制列表和能力——并分析它们对权限委托、撤销以及对经典漏洞的易感性等关键动态的深远影响。然后,在“应用与跨学科联系”一章中,我们将看到这些原理的实际应用,揭示访问矩阵如何支撑从操作系统内核和云虚拟化到智能家居和社交网络等一切事物的安全,为驯服数字复杂性提供了一种统一的语言。

原理与机制

宏伟构想:权力矩阵

我们如何推理安全性?在物理学世界中,我们常常从一个宏大、简化的思想——比如守恒定律——开始,然后探索其后果。在计算机保护的世界里,我们也可以这样做。这个宏伟的构想惊人地简单:想象一个巨大的网格、一张表格,或者我们称之为​​访问矩阵​​。

在这个矩阵的行上,我们列出系统中的所有活动实体——“谁”。这些可以是像 Alice 和 Bob 这样的用户,甚至是代表他们运行的程序。我们称这些为​​主体 (subjects)​​。

在列上,我们列出所有需要保护的东西——“什么”。这些可以是文件、打印机,甚至其他程序。我们称这些为​​客体 (objects)​​。

矩阵的单元格里放的是什么呢?是“如何做”。每个单元格,比如在 Alice 的行和“Budget.xlsx”的列的交点处,包含一个特定操作的列表,即 Alice 被允许对该文件执行的​​权限 (rights)​​。她能读取它吗?写入它吗?执行它吗?矩阵持有所有答案。如果 Alice 对预算文件有 read 权限,那么条目 M[Alice,Budget.xlsx]M[\text{Alice}, \text{Budget.xlsx}]M[Alice,Budget.xlsx] 将包含 {r}\{r\}{r}。如果 Bob 没有任何权限,他对应的条目将为空,即 ∅\varnothing∅。

这个​​访问矩阵​​是我们的通用模型。它是一个系统完整保护状态的完美、抽象的快照。无论系统多么复杂,原则上我们都可以用这个简单、优雅的结构来描述其安全策略。然而,真正的乐趣始于我们试图将这个美丽的抽象付诸实践之时。

将矩阵付诸实践:两种哲学

将白板上的矩阵构建到操作系统结构中是另一回事。事实证明,主要有两种“切分”矩阵的方式,这两种方法代表了两种根本不同的安全哲学。

客体作为守门人:访问控制列表

实现矩阵的一种方法是垂直地、逐列地切分它。对于每个客体,我们创建一个列表,其中包含所有对其拥有权限的主体。这个附加在客体自身的列表被称为​​访问控制列表 (ACL)​​。

想象一个专属俱乐部。俱乐部本身(客体)门口有一个保镖,手里拿着一份名单(ACL)。当你(主体)试图进入时,保镖会核对名单上的你的名字,看你是否被允许进入。权力掌握在客体的守门人手中。大多数我们熟悉的操作系统,如 Windows 和类 Unix 系统,都严重依赖这种哲学。当你列出文件属性时看到的权限,本质上就是一个 ACL。

主体作为持钥人:能力列表

另一种方法是水平地、逐行地切分矩阵。对于每个主体,我们创建一个它能访问的所有客体的列表。但这不仅仅是一个简单的列表;它是一个特殊、不可伪造的令牌集合,称为​​能力 (capabilities)​​。

一个​​能力 (capability)​​ 就像一把钥匙。它是一个单一、统一的令牌,指明了一个客体以及你对其拥有的特定权限。为了让 Alice 读取预算文件,她需要拥有一个能力,可以表示为一个元组:(obudget,{r})(o_{\text{budget}}, \{r\})(obudget​,{r}),其中 obudgeto_{\text{budget}}obudget​ 是该文件的唯一、秘密的标识符。

在这种模型中,主体是持钥人。要进入俱乐部,你不用告诉保镖你的名字,你只需出示正确的钥匙。权力由主体持有。这种方法在主流桌面操作系统中不太常见,但却是许多安全研究系统和微内核的基础。

乍一看,ACL(列)和能力列表(行)似乎是同一枚硬币的两面——只是存储我们主矩阵中相同信息的不同方式。对于一个静态、不变的系统,它们是等价的。但系统不是静态的。一旦权限开始变化,这两种哲学之间深刻的动态差异就变得清晰起来。

委托与撤销之舞

当权限被传来传去时,一个安全系统的真正特性就显现出来了。让我们来探讨一下当我们试图授予或撤销权限时会发生什么。

想象一个简单的策略:一个能读取文件的主体也可以将该读取权限授予他人。我们从只有一个主体 s1s_1s1​ 拥有读取一个机密文件 o⋆o^\staro⋆ 的权限开始。随着时间的推移会发生什么?s1s_1s1​ 可以将权限授予 s2s_2s2​。现在 s1s_1s1​ 和 s2s_2s2​ 都可以授予该权限。他们又将其授予 s3s_3s3​。很快,这个权限就像瘟疫一样蔓延,直到系统中的每个主体都能读取该文件。我们最初的保密目标完全丧失。这个简单的思想实验表明,不受控制的委托是一个严重的威胁。

我们的两种哲学如何处理这个问题呢?在 ACL 系统中,委托通常是一件受控的事情。要给朋友授予访问权限,你通常不能自己编辑 ACL。你需要一个特殊的管理权限——我们称之为“授予”或“复制”权限——来修改 ACL。如果一个策略要给非所有者一个复制特权(比如一个 x⋆x^{\star}x⋆ 权限,允许他们将 xxx 授予他人),这可能会创建一个绕过所有者控制的意外委托链。一个健壮的策略会确保只有客体的所有者才持有如此强大的管理权限。

在 ACL 系统中,撤销——收回权限——非常直接。所有者只需告诉保镖把一个名字从名单上划掉。这个变更是即时且绝对的。

在能力系统中,情况则截然不同。能力的天性就像信息一样:它可以被复制。如果 Alice 有一个能力(一把钥匙),她通常可以复制一份并交给 Bob。这非常灵活和强大,但也带来了一个巨大的难题:​​撤销​​。如果 Alice 后来决定 Bob 不应该再有访问权限,她能做什么呢?Bob 已经有了自己的钥匙副本。更糟糕的是,Bob 可能已经为他的朋友 Carol 复制了一份。撤销访问权限意味着找到并销毁该能力的每一个副本,这在一个复杂的系统中,如果不构建复杂(且昂贵)的间接层,几乎是一项不可能完成的任务。

灵活委托和有效撤销之间的这种根本性张力,是安全系统设计中的一个核心主题。

权力的阿喀琉斯之踵:困惑的代理人问题

最著名和最微妙的安全漏洞之一是​​困惑的代理人问题 (confused deputy problem)​​。想象一下操作系统中的一个强大服务,比如一个备份工具。这个工具以高权限运行,允许它读取系统上的任何文件以进行备份。我们称这个服务为我们的“代理人”。现在,一个低权限用户——一个“客户端”——请求代理人备份一个文件,但他们没有提供自己文件的路径,而是提供了系统密码文件的路径。

这个代理人有点迟钝,看到了这个请求。它有权限读取任何文件,所以当它被要求读取密码文件时,系统会说:“当然,你有权限。” 代理人读取文件并尽职地将数据交还给恶意客户端。代理人被迷惑而滥用了它的权力。

这个问题是具有​​环境权限 (ambient authority)​​ 系统的典型症状。备份服务拥有其权限仅仅是因为它是谁。这在基于 ACL 的系统中很典型,其中该服务的用户账户会出现在每个文件的 ACL 上。

这就是能力哲学的闪光之处。在一个设计良好的能力系统中,代理人没有环境权限。它以最小权限运行。客户端要备份一个文件,必须传递给代理人一个针对该文件的能力——一把特定的钥匙。代理人随后可以使用这把钥匙,也只能用这把钥匙,来执行备份。如果一个恶意客户端想要密码文件,他们需要提供一个针对该文件的能力。但他们没有!所以他们无法欺骗代理人。攻击被当场阻止。这种对​​最小权限原则 (Principle of Least Privilege)​​——即只给予程序完成当前任务所必需的精确权限——的完美执行,是支持基于能力的设计的最有说服力的论点之一。

构建这样一个系统的强大方法是通过​​域分离 (domain separation)​​。例如,一个需要读取机密配置文件并同时写入公共日志文件的守护进程,可以被拆分成两个更小的、隔离的域。一个域 DcD_cDc​ 只持有读取配置文件的能力。另一个域 DlD_lDl​ 自身没有任何能力,但接受来自客户端的请求,客户端必须传递给它一个针对他们想要写入的特定日志文件的能力。这样,读取配置文件的敏感权限就永远不会暴露给面向客户端的程序部分,从而大大降低了被迷惑的风险。

变形:域与权限提升

到目前为止,我们都将主体视为静态实体。但实际上,一个程序所需的权限在其生命周期中是可能变化的。这就引出了​​保护域 (protection domain)​​ 的概念,它就是一个主体在任何给定时刻所拥有的权限集合——也就是访问矩阵中该主体的当前行。

有时,一个程序需要临时获得更多权力。这被称为​​域切换 (domain switching)​​。这就像一个平民穿上警官的制服;在短时间内,他们可以行使随之而来的权力。在实际应用中最著名的例子是类 Unix 系统中的 ​​setuid​​ (Set User ID) 机制。一个普通用户可以执行一个特殊程序,在该程序运行期间,进程会获得程序所有者的身份和权限,而所有者可能是拥有最高权力的 'root' 管理员。

这种临时的权力获取被称为​​权限放大 (rights amplification)​​。在我们的访问矩阵模型中,我们可以将其表示为一个特殊规则。如果域 DuD_uDu​ 中的一个用户执行了由用户 vvv 拥有的 setuid 程序 PPP,系统允许该进程从域 DuD_uDu​ 切换到域 DvD_vDv​。在 DvD_vDv​ 中运行时,该进程可以做任何 vvv 能做的事情,比如读取一个之前对 uuu 不可访问的文件 FFF。

这个机制可以用惊人的精度来建模。POSIX 中文件的权限模式,比如八进制代码 6750,就是一个用于域切换的紧凑配方。这个代码指定了 setuid 和 setgid 位,以及所有者和组的读/写/执行权限。当来自一个域的用户执行这个文件时,这些位精确地决定了进程将进入哪个新域——拥有新的有效用户 ID 和组 ID。

当然,危险在于临时的权限放大可能被利用来创造一个永久的权限放大。一个进程在临时以 'root' 身份运行时,可以编辑访问矩阵本身——例如,通过将原始用户添加到一个特权组中。即使在 setuid 程序退出、域切换回来之后,那个新的、永久的权限仍然存在。这就是许多现实世界中权限提升攻击的本质。

魔鬼在细节中:实现挑战

一个漂亮的 模型是不够的。整个系统的完整性取决于其无懈可击的实现。其中有两个特别的挑战尤为突出。

不可伪造性与信任问题

如果我们选择能力哲学,就必须确保我们的“钥匙”是​​不可伪造的 (unforgeable)​​。如果一个能力只是存储在程序内存中的一对数字 (o,ρ)(o, \rho)(o,ρ),那么如何阻止程序更改权限部分 ρ\rhoρ呢?一个拥有 read 能力的程序可以简单地篡改它,创造出一个 {read, write} 的能力,从而立即提升自己的权限。依赖于客体标识符“不可猜测”并非一种防御手段;程序已经知道了有效的客体标识符。

对此有两个稳健的解决方案。第一种是完全不信任用户程序。内核将所有真正的能力保存在自己的受保护内存中,只给用户程序不透明的句柄——像文件描述符这样的无意义数字。当程序想要使用一个能力时,它传递这个句柄,内核会在其秘密表中查找真实的、未被篡改的能力。第二种解决方案使用密码学。内核可以通过附加一个密码学的​​消息认证码 (MAC)​​ 来“封装”一个存储在用户空间的能力,该 MAC 使用只有内核知道的密钥计算。如果用户程序修改了能力,MAC 将不再匹配,内核会将其作为伪造品拒绝。

原子性与时间问题

第二个挑战是时间本身。访问矩阵代表一个快照,但系统是一部电影。如果在操作中途规则发生变化会怎样?这会导致一个经典的竞争条件,称为​​检查时-使用时 (Time-Of-Check-To-Time-Of-Use, TOCTTOU)​​。

想象一下,内核检查 Alice 是否有权限写入一个文件(“检查”)。检查通过了。但在内核实际执行写入(“使用”)之前的微秒内,管理员撤销了 Alice 的权限。如果内核继续执行写入,它就基于过时的信息采取了行动,违反了安全策略。

这在具有动态策略的系统中是一个特别棘手的问题,例如组成员身份随时可能改变。为了解决它,“检查”和“使用”必须被绑定在一个​​原子 (atomic)​​ 操作中——一个看起来是瞬时发生、不可能被中断的操作。实现这一点的一种方法是使用版本控制。内核可以为策略的每一部分(如 ACL 或用户的组列表)关联一个版本号,或称为​​代数计数器 (generation counter)​​。当它检查一个权限时,它会记录当前的版本号。在使用该权限之前,它会重新检查版本号。如果版本号已更改,意味着策略已被修改,操作必须被中止或重新授权。

终极前沿:我们能证明一个系统是安全的吗?

这就引出了最后一个深刻的问题。给定一个访问矩阵系统的完整描述及其所有更改权限的规则,我们能否编写一个程序,确切地告诉我们某个给定用户是否有可能获得某个特定的危险权限?这被称为​​安全问题 (Safety Problem)​​。

如果能有这样一个安全检查器来自动发现我们设计中的安全漏洞,那将是极好的。但是, Harrison, Ruzzo, 和 Ullman 在 20 世纪 70 年代的一项里程碑式成果表明,对于一个通用系统——一个可以创建新主体和新客体的系统——安全问题是​​不可判定的 (undecidable)​​。它等价于可计算性理论中著名的停机问题 (Halting Problem)。不存在任何单一算法能保证解决所有可能系统中的该问题。我们预测安全状态未来的能力从根本上是有限的。

但这里有一个精妙的转折。这个不可判定性结果适用于一般情况。如果我们对系统加以约束,问题可以再次变得可判定。例如,在一个单调(权限从不被撤销)且主体和客体数量固定有限的系统中,可能状态的总数是有限的。在这种情况下,我们原则上可以遍历所有可达状态,并确定其中是否存在危险状态。

这不仅仅是一个理论上的好奇。它教给我们一个关于设计的深刻教训。通过选择更简单、更受约束的保护模型,我们从一个不可判定的混乱世界走向一个具有数学确定性的世界。对安全的追求不仅仅是建造更高的围墙,而是设计出结构如此清晰和简单,以至于我们能够有效地对其进行推理——甚至可能证明它们是安全的系统。

应用与跨学科联系

在了解了访问矩阵的抽象原理和机制之后,你可能会想:这只是一个精巧的理论棋盘,还是它真的构建了我们周围的世界?答案是响亮的“是”。主体、客体和权限组成的简单网格不仅仅是一项学术练习;它是我们使用的几乎每个数字系统中安全和秩序的秘密蓝图。其深远的影响从微处理器的最深层硅谷一直延伸到社交网络和物联网的庞大数字生态系统。在本章中,我们将探索这一广阔的领域,看看访问矩阵的优雅逻辑如何驯服现代计算的狂野复杂性。

机器之心:操作系统内核

访问矩阵的第一个也是最根本的应用领域是操作系统 (OS) 内核——协调你计算机一切活动的主程序。在这里,矩阵不是一个选项,而是生存的必需品。

想象一下内核在两个程序之间管理共享内存。在我们的抽象模型中,我们可以授予一个进程读取 (rrr)、写入 (www) 或映射 (mmm) 一段内存的权限。现在,考虑一个由计算机硬件施加的奇特、现实的约束:内存管理单元 (MMU) 可以将一页内存设置为“只读”或“读写”,但它没有“只写”的概念。这个物理限制迫使操作系统设计者做出选择。如果一个程序只被授予 www 权限,内核如何强制执行它?它不能。为了允许写入,它必须将硬件保护设置为“读写”,这意味着该程序也能读取。因此,操作系统必须采取一种策略,即授予写入权限就隐含地需要授予读取权限。访问矩阵的抽象之美必须向机器的物理定律低头,这是逻辑策略与具体现实之间对话的完美例子。

当处理外部硬件设备时,内核面临的挑战成倍增加。像网卡或存储控制器这样的外围设备是强大但不可信的伙伴。它们可以执行直接内存访问 (DMA),在没有 CPU 参与的情况下直接写入系统内存。一个错误或恶意的设备可能会损坏整个操作系统。为了驯服这些野兽,现代系统使用输入输出内存管理单元 (IOMMU),这是一个转换设备内存地址的硬件守门人。以能力系统形式实现的访问矩阵为 IOMMU 提供了完美的约束。为了执行 DMA 操作,设备驱动程序必须出示不是一个,而是两个能力:一个证明它对设备有权限,另一个证明它对目标内存区域有权限。这种优雅的设计防止了“困惑的代理人”场景,即一个设备可能被欺骗写入属于另一个设备的内存。驱动程序必须证明其对行动的发起者和目标都拥有权限,这是由访问矩阵在硬件中强制执行的一个强大安全模式。

矩阵的作用超越了保密性和完整性,延伸到确保可用性。考虑一个微内核,其中高优先级客户端 CHC_HCH​ 和低优先级客户端 CLC_LCL​ 都通过同一个通信端点向服务器 SSS 发送请求。如果端点队列是严格的先进先出, CHC_HCH​ 可能会被卡在 CLC_LCL​ 后面等待,造成“优先级反转”,从而导致拒绝服务。解决方案在于构建访问矩阵本身。通过创建两个独立的端点 EHE_HEH​ 和 ELE_LEL​,并且只将 EHE_HEH​ 上的 sendsendsend 权限授予高优先级客户端,将 ELE_LEL​ 上的权限授予低优先级客户端,我们利用访问矩阵来构建独立的通信渠道。服务器现在可以通过总是先检查 EHE_HEH​ 上的消息来进行优先级排序,从而保证高优先级工作永远不会被低优先级的闲聊所阻塞。在这里,访问矩阵成为一种流量整形和确保系统响应能力的工具。

建造高墙与桥梁:虚拟化和容器

从单一机器扩展开来,访问矩阵为在单一物理计算机内构建整个数字世界提供了蓝图。

你每天使用的云建立在虚拟化之上,其中虚拟机监视器 (VMM) 或 hypervisor,在完全隔离的情况下运行多个客户操作系统。这种隔离是如何实现的?访问矩阵再次提供了模型。每个客户操作系统 (GiG_iGi​) 是一个主体,其内存 (MiM_iMi​) 是一个客体。矩阵被配置为给予 GiG_iGi​ 对 MiM_iMi​ 的全部权限,但对任何其他客户的内存 MjM_jMj​ 权限为空集。但客户如何管理自己的内存映射呢?直接授予它们映射权限是危险的。一个更健壮的设计引入了一个受 hypervisor 控制的可信“映射服务”客体。每个客户都被赋予一个不可转让的能力来调用此服务。当客户 GiG_iGi​ 请求服务映射一个页面时,该服务——作为一个警惕的代理——强制执行策略,即映射只能以 GiG_iGi​ 自己的内存为目标。这种中介架构完美地强制执行了作为云计算基石的严格隔离。

最近,轻量级容器彻底改变了软件开发。这凸显了从粗粒度的环境权限到细粒度的客体能力的演进过程。在 Linux 中,一个进程可能被授予一个强大的环境能力,如 CAP_NET_ADMIN,使其对网络拥有广泛的权力。这违反了最小权限原则。一个受客体能力模型启发的、好得多的设计是“削弱”这种权力。容器运行时可以创建一个特殊的 netlink 套接字,该套接字被过滤(使用像 BPF 这样的技术)以仅接受配置特定网络接口的命令。然后它将此套接字的文件描述符——一个不可伪造的内核令牌——传递给容器。该容器没有环境 CAP_NET_ADMIN 权限;它所拥有的只是这一个单一的、受限的能力。它已从“网络管理员”降级为“一个配置 eth0 的票证持有者”。这种对攻击面的优雅缩减,是访问矩阵思想在现代基础设施中的直接应用。

数字社会:互联世界中的应用

访问矩阵不仅存在于数据中心;它也存在于你的客厅、你的社交信息流,甚至在从文档中复制文本的简单行为中。

考虑一个智能家居场景,你希望授予客人临时权限以解锁前门和控制灯光。即使你家的互联网连接断开,此访问也必须有效。一个要求每个操作都需由云服务器批准的集中式访问控制列表 (ACL) 系统会失败。解决方案是基于能力的设计。房主的应用程序可以生成一个加密令牌——一个能力——它经过签名,包含客人的身份、特定设备(例如,“前门”)、允许的权限(例如,unlock)和有效期。然后,客人可以直接向门锁出示此令牌,门锁可以在本地验证其真实性和时间限制,无需联系中央服务器。这是在常常不可靠的物联网世界中进行分布式授权的健壮设计。

在社交网络中,“分享”帖子是一种权限委托形式。不受控制的分享可能导致私人信息的病毒式传播。我们如何限制这一点?一个巧妙的能力设计可以编码一个“衰减计数器”。当你第一次分享帖子时,接收者会收到一个带计数器的能力,比如 ℓ=5\ell=5ℓ=5。当他们分享时,他们的接收者会得到一个 ℓ=4\ell=4ℓ=4 的能力。这个过程一直持续到计数器达到零,此时分享的权限就用尽了。这实现了一个有界传播限制,优雅地控制了权限的传播,而不仅仅是其初始存在。这是一种每一步都会减弱的权限,是在社交图谱中管理信息流的美妙机制。

即使是复制粘贴这种平凡的行为也受这些原则的支配。当你复制敏感文本时,它被放置在一个剪贴板客体中。当你将其粘贴到另一个应用程序中时,操作系统必须授予该应用程序一个临时的读取剪贴板的权限。但如果目标应用是恶意的呢?它可能会读取数据并立即将其转发给攻击者。一个简单的能力是不够的。一个健壮的解决方案是将一个高度限制的能力(一个不可委托且只能使用一次的能力)与一个强制访问控制 (Mandatory Access Control, MAC) 系统相结合,该系统用一个保密性标签来“污染”数据。然后,操作系统可以强制执行一个基本规则:“高保密性”数据不能被写入“低保密性”的目的地,从而防止泄漏。这展示了访问矩阵模型如何与更深层次的信息流控制安全问题相联系。

驯服复杂性:角色、规则和规模

随着系统发展到包含成千上万的用户和数百万的客体,逐个单元格地管理访问矩阵变得不可能。然而,该模型提供了强大的抽象来管理这种复杂性。

一个关键挑战是性能。在一个拥有数千用户和数万数据列的大型数据湖中,我们应该使用每列一个 ACL 还是每用户一个能力列表?答案取决于访问模式。如果许多用户需要访问同一组列,使用 ACL 会导致巨大的列表,检查起来很慢。如果许多用户共享相同的权限集(例如,“市场”部门的所有分析师都获得相同的访问权限),将他们的权限分组到一个单一的“视图”中,并为该视图颁发一个能力,效率可能会高得多。选择矩阵的列式(ACL)表示还是行式(能力)表示,是一个由数据本身的结构决定的关键工程权衡。

一个更为关键的抽象是角色的概念。在医院里,每次有新医生值班时,都要编辑数千份病历上的 ACL,这将是一场噩梦。取而代之的是,系统使用基于角色的访问控制 (Role-Based Access Control, RBAC)。病历上的 ACL 将访问权限授予一个抽象的角色,比如“值班心脏病专家”。另外,一个小的、集中的列表将特定的医生映射到该角色。当轮班变更时,管理员只做一个微小的改动:他们更换分配给该角色的医生。这个单一操作会即时且全局地更新医院里每份记录的有效权限。这种间接性——管理角色的权限而不是人的权限——是驯服管理复杂性的重要策略。

这引导我们走向安全设计的顶峰:权限分离。考虑一个强大的、单体的程序,比如一个软件包管理器,它需要执行许多敏感操作。与其将整个程序作为超级用户运行,我们可以将其分解为一个由多个小的、无特权的辅助进程组成的工作流。一个辅助进程只知道如何从网络上获取文件;另一个只知道如何验证加密签名;第三个只知道如何将文件写入特定目录。一个中心的、受信任的代理来协调这个工作流,在恰当的时机为每个辅助进程生成细粒度的、临时的能力。网络辅助进程获得一个只能连接到已知仓库的能力。文件写入者获得一个只能写入目标目录的能力,并且只有在签名检查器成功之后。这种在访问矩阵指导下的权力分解,极大地缩小了攻击面。

最后,能力模型帮助我们驯服一个微妙但普遍存在的危险:环境权限。这是指程序仅仅凭借其环境而非明确授予而拥有的任何权力。一个经典的例子是全局 DNS 解析器。一个能建立网络连接的程序通常拥有查询任何域名的环境权限。如果我们想限制一个不受信任的插件只能连接到 payments.example.com,那么给予它访问全局解析器的权限就太大了。能力的解决方案是不给它访问全局解析器的权限。取而代之的是,我们给它一个针对特殊的、受限的解析器客体的能力,该客体只能解析一个名字:payments.example.com。该插件的权限不再是环境的;它是明确的、具体的和最小的。

一个统一的愿景

从硬件的物理约束到社交网络的抽象规则,访问矩阵提供了一种单一、统一的语言来推理保护问题。它允许我们在虚拟机之间建立壁垒,为家中的客人创建临时通行证,管理专业人士的动态角色,并小心地分解我们最特权软件的权力。它的美不在于其复杂性,而在于其简单性,以及它为我们所居住的这个充满活力、混乱和互联的数字世界带来可预测秩序的非凡能力。