
在当今这个由云服务和分布式系统主导的时代,一个根本性的信任问题浮出水面:当我们在非自己所有或控制的硬件上处理敏感数据时,我们如何保护这些数据?传统的安全措施侧重于保护静态数据(在磁盘上)和传输中的数据(在网络上),却在使用中的数据(在内存中)方面留下了一个关键漏洞。机密计算通过创建可验证的、硬件隔离的环境来直接解决这一差距,在这样的环境中,代码和数据可以免受底层基础设施(包括云提供商自己的管理员)的侵害。
本文将带领读者深入机密计算的复杂世界,深入探讨其基本概念和深远影响。它揭开了在零信任环境中进行计算的技术的神秘面纱,从而改变了我们在现代计算中处理安全问题的方式。您将了解使这一切成为可能的核心原则,以及这项技术与更广泛的计算机科学领域之间的深刻联系。
首先,在“原理与机制”一节中,我们将剖析可信执行环境(TEE)的结构,探索它如何从根本上减少可信计算基、强制执行内存隔离,并使用远程证明提供安全性的加密证明。随后,“应用与跨学科联系”一节将拓宽我们的视野,揭示机密计算如何重塑操作系统中由来已久的概念,如何在云中实现安全虚拟化,以及如何为在全球范围内进行安全的协同计算开启新的可能性。
想象一下,你需要执行一项高度敏感的计算,比如分析一份机密的医疗记录,或者管理加密货币钱包的私钥。你可以在自己可信的、位于上锁房间里的计算机上运行这个程序。但如果这项计算需要庞大的云数据中心的算力,而这台机器不为你所有,由你不认识的人运行,并且与无数其他用户共享,情况又会如何?你如何能相信,当你的数据正在被处理时,没有人——无论是云提供商、流氓管理员,还是同一台机器上的其他用户——正在窥探你的数据?
这正是机密计算旨在解决的核心挑战。答案不是去信任这台机器,而是从中划分出一小块我们能够信任的部分,一个为我们的代码和数据打造的数字堡垒。这个堡垒被称为可信执行环境(Trusted Execution Environment, TEE),或称安全飞地(secure enclave)。
机密计算的基本原则是大幅缩减可信计算基(Trusted Computing Base, TCB)。TCB 是系统安全所依赖的所有硬件和软件组件的总和。在传统计算机中,TCB 非常庞大:CPU、主板、固件、操作系统(OS)及其所有驱动程序。其中任何一个组件的缺陷都可能危及整个系统。
安全飞地颠覆了这种模式。其目标是让 TCB 在物理上尽可能小:理想情况下,仅限于处理器芯片本身。其他所有东西——操作系统、虚拟机监视器(hypervisor)、设备驱动程序、固件——都被认为在 TCB 之外,因而是不可信的。操作系统不再是机器的最高统治者;它只是 CPU 必须监管的另一个潜在的恶意程序。
这种观念上的根本性转变对系统如何运作产生了深远的影响。从飞地的角度来看,强大的操作系统被降级为一个纯粹的“顾问”,一个可以提供服务但其一举一动都必须被怀疑的助手。
内存保护: 你可能认为操作系统通过管理页表来控制内存。但在一个机密计算系统中,CPU 硬件本身成为了飞地内存门口的终极“保镖”。当操作系统试图为飞地映射一页内存时,CPU 的内存管理单元会用一个特殊的、不可见的标签标记该页。随后,任何在飞地之外的代码——即使是运行在最高特权内核模式下的操作系统——试图访问该页面的行为,都会被硬件以一句“你不在名单上”而明确阻止。
CPU 调度: 操作系统仍然控制着调度器,决定哪个程序何时运行。一个敌对的操作系统可以干脆拒绝调度飞地的代码,从而导致拒绝服务攻击。因此,飞地的编写必须基于这样一种理解:操作系统的调度仅仅是一个“性能提示”。它的安全性乃至持续可用性都不可依赖。
输入/输出: 如果飞地需要读取文件或发送网络消息怎么办?它必须请求操作系统来完成。这就像从城堡的窗户向下面庭院里的信使大声下令。一旦数据离开飞地受保护的内存,进入操作系统的领域,它就完全暴露了。操作系统可以读取、修改它,或者将其发送到错误的目的地。因此,飞地永远不能将明文数据托付给操作系统。所有离开堡垒的数据都必须加密以保证机密性,并进行加密签名以保证完整性和真实性。一个文件名,如 /path/to/my_secret,只是操作系统提供的一个标签;飞地必须对文件的内容进行加密验证,以确保它没有被换成恶意的东西。
在我们开始信任飞地的硬件堡垒之前,我们有一个更根本的问题:我们如何知道硬件本身处于可信状态?如果一个高明的攻击者在系统启动过程中攻破了防线,他们就能禁用飞地所依赖的硬件保护机制。
解决方案是建立一条信任链,从一个绝对确定的点开始。这个过程通常被称为安全启动(Secure Boot),其工作方式就像一连串的验证反应。
它始于一个信任根,通常是一小段永久蚀刻在 CPU 硅片或只读存储器(ROM)芯片中的代码。我们信任这段代码,因为它是不可变的;它无法被更改。
当计算机开机时,这段不可变的代码首先运行。它唯一的工作就是验证启动序列中的下一个软件,比如说主固件(UEFI)。它通过检查数字签名来完成这一任务。就像画作上的签名证明其作者一样,数字签名证明该固件是由合法的硬件供应商创建且未经篡改的。
这个序列创建了一条加密链,其中每个环节都为下一个环节作保。到你的操作系统运行时,你已经有了一个强有力的保证:从第一条指令到完整的内核,整个软件栈都是真实且未经篡改的。现代系统甚至还包括回滚保护,使用特殊的硬件计数器来确保攻击者无法欺骗系统启动一个旧的、虽有签名但已知存在漏洞的组件版本。这条信任链是构建飞地安全不可或缺的基础。
现在我们有了一个在经过验证的软件基础上运行的可信硬件堡垒。那么,应用程序实际上是如何使用它的呢?将代码和数据移入和移出飞地是一个精心编排的舞蹈,由硬件本身进行协调。
飞地代码与主应用程序在相同的低权限“用户模式”下运行;它没有获得特殊权力。进入飞地安全世界的转换由一个特殊的硬件指令触发,通常称为 ECALL(Enclave Call,飞地调用)。这不是一个常规的函数调用;这是一个上下文切换,CPU 在此期间检查权限,进入“飞地模式”,并开始在受保护的边界内执行代码。
如果飞地需要执行一个特权操作,比如打开一个网络套接字怎么办?它不能。在飞地内部尝试执行系统调用指令会触发硬件故障。相反,飞地必须执行一个 OCALL(Outside Call,外部调用)。这是另一个特殊的指令,可以安全地从飞地模式转换出来,将控制权交还给不可信的主机应用程序。然后,主机应用程序代表飞地向操作系统发出正常的系统调用。
由于飞地的内存对系统的其他部分来说是一个黑匣子,数据不能直接共享。在 ECALL 期间传入飞地或从 OCALL 返回的任何数据都必须小心翼翼地跨越边界进行复制。这个打包和复制的过程称为编组(marshalling)。
ECALL、OCALL 和编组这支错综复杂的舞蹈是有代价的。每当信任边界被跨越时,CPU 都必须执行一系列复杂的操作:保存当前世界的状态,加载另一个世界的状态,刷新内部流水线,并执行安全检查。一个单一的缺页故障——即操作系统需要从磁盘加载一块内存——就可能触发一次“异步飞地退出”,耗费数万个 CPU 周期,这个延迟可以用微秒来衡量。这就是机密计算的根本权衡:我们获得了强大的安全性,但代价是跨越护城河的操作性能会下降。
我们现在来到了机密计算最神奇的能力。你坐在办公室里,如何能确定你发送到远程云服务器的代码确实在一个飞地内安全运行,而不是某个巧妙的仿冒品?答案是远程证明(remote attestation)。
这个过程始于度量(measurement)。当飞地被加载到其受保护的内存中时,CPU 内部一个专用的硬件引擎会计算飞地初始代码和配置的加密哈希值(一个独特的数字指纹)。这个度量值随后被存储在 CPU 内部一个受特殊保护的寄存器中。
现在,飞地可以请求硬件(CPU 和一个称为可信平台模块 Trusted Platform Module, TPM 的独立安全芯片的组合)生成一份证明报告(quote)。这份报告是一个经过数字签名的数据结构,包含了度量值。签名是使用一个对该特定硬件唯一并在制造时嵌入的私钥创建的。
这份签了名的证明报告就是飞地的加密护照。它可以被发送给任何远程方,然后远程方可以:
如果签名有效且哈希值匹配,远程方就获得了加密证明,证实他们精确的、未经修改的代码正在那台特定机器上的一个硬件保护的飞地内运行。这个机制使我们能够在没有任何物理接触的情况下建立信任。正是这种证明能力,将 TEE 从一个本地安全特性转变为安全云计算的基石。
这种度量和记录的过程也是“度量启动”(Measured Boot)的基础。TPM 中的一个平台配置寄存器(PCR)扮演着一个防篡改的日志簿。启动链中的每个组件度量下一个组件,并用结果扩展 PCR:$v_{new} = H(v_{old} || m_{new})$。由于这个操作是顺序相关的,最终的 PCR 值是事件确切序列的指纹。任何偏差——一个不同的组件,甚至是相同组件但顺序不同——都会产生一个完全不同的最终值,使得任何篡改对于远程验证者来说都显而易见。
并非所有的 TEE 都是以相同方式构建的。两种主流的架构哲学通常被称为“单世界”和“双世界”设计。
尽管有这些强大的保护,机密计算并非万能灵药。它是一场持续的安全军备竞赛的一部分。TCB 的定义——你必须信任的组件集合——本身就突显了其局限性。如果 TCB 内部的某个组件有漏洞怎么办?
想象一个经过签名、验证和证明的内核驱动程序,它有一个像缓冲区溢出这样微妙的内存安全漏洞。我们所有的启动时和加载时检查都会通过。远程验证者会收到一份完美的证明报告。然而,攻击者可以在运行时发送一个畸形的输入来利用这个漏洞,从而劫持这段“可信”代码的控制流。这告诉我们,“可信”不等于“无懈可击”。
这个现实迫使我们采用纵深防御(defense in depth)。
进入机密计算的旅程揭示了一场在安全与性能、信任与验证之间美丽而复杂的舞蹈。它推动了计算机体系结构的边界,要求我们批判性地思考我们将信任置于何处以及如何验证它,从而构建层层防御,以在一个日益不可信的世界中保护我们最敏感的数据。
在领略了可信执行环境(TEE)的精妙机制之后,我们现在到达了一个令人振奋的制高点。从这里我们可以看到,机密计算不仅仅是安全专家工具箱中的又一个工具。它是一场构造板块式的转变,是对软件与硬件之间关系的根本性反思,其影响波及整个计算机科学领域。就像一条新的物理定律,它的发现迫使我们重新审视旧的假设,并解锁了我们曾认为不可能的现象。让我们来探索这个新世界,不将其视为技术的目录,而是作为一场思想的旅程,由可验证的信任这一美妙而简单的原则统一起来。
计算机安全的核心是一个既简单又深刻的概念:可信计算基(TCB)。想象一位中世纪的国王希望保护他的王冠。TCB 就是他必须信任的所有人的集合——他的卫兵、他的顾问、他的厨师。如果其中任何一个人不忠或无能,王冠就岌岌可危。一位明智的国王知道,他必须无条件信任的人越少,他就越安全。软件亦是如此。TCB 是其正确性对执行安全策略至关重要的所有硬件、固件和软件组件的总和。TCB 中的每一行代码都是一个可能因恶意或错误而背叛系统的卫兵。整个安全系统设计的历史可以被看作是一场旨在缩小这个可信“王国”规模的崇高追求。
这场追求塑造了操作系统的整体架构。传统的单体内核将几乎所有服务——驱动程序、文件系统、网络栈——都捆绑到一个单一的特权程序中,其 TCB 极其庞大。整个内核,通常是数千万行代码,都必须被信任。作为回应,设计者创造了微内核,它将大部分服务委托给非特权的用户空间服务器,只在可信内核中保留一小部分核心的基本功能。还有人设想了外核(exokernel),通过将几乎所有的抽象都移到非特权的库中,进一步缩小 TCB,让内核只负责安全地复用原始硬件。每种设计都是为了减少国王必须信任的卫兵数量的不同策略。
这个挑战在构建编译器的问题中达到了其在智识上最纯粹的形式。你如何能信任一个将人类可读的源代码翻译成机器指令的编译器?更令人烦恼的是,你如何能信任一个编译自身的编译器?这正是 Ken Thompson 著名的“关于信任之反思”(Reflections on Trusting Trust)演讲的主题。一个恶意的编译器可以秘密地在它正在编译的新版自身中插入一个后门,这个后门将永远存在,在任何源代码中都不可见。唯一真正的防御是从一个“种子”编译器或解释器开始构建整个信任链,这个种子必须足够小和简单,以至于可以通过形式化验证或人工审计。这个可信的种子是整个软件生态系统的最小 TCB。机密计算为这个古老的追求提供了一个惊人优雅的、基于硬件的答案。它允许我们定义一个极小的 TCB:仅包含我们的应用程序代码和 CPU 本身,从而将数百万行的操作系统代码从信任圈中精准地剔除出去。
这个新现实迫使我们重新思考操作系统的角色。几十年来,操作系统一直是绝对的君主,一个拥有对每个进程和每个字节内存完全权威的特权实体。有了机密计算,操作系统被降级了。它仍然是领域的管理者——它调度线程、管理页表、控制设备——但它再也无法窥探其臣民(即飞地)的私人城堡。
这种新的“社会契约”带来了有趣的后果。在一个令人愉快的转折中,操作系统可以利用这项技术来保护自己。操作系统有自己的皇冠珠宝,比如全盘加密的主密钥。传统上,这些密钥位于内核内存的某个地方,容易受到复杂攻击。通过使用 TEE,操作系统可以将其密钥库放入一个飞地内,从而成为其自身硬件安全能力的客户。TEE 的架构选择至关重要。一个使用像 Intel SGX 这样的用户空间 TEE 的操作系统,必须通过委托给用户空间中的一个辅助进程来与其密钥库通信,这会因上下文切换而产生性能成本。然而,一个在具有 ARM TrustZone 平台上运行的操作系统,可以将其密钥库放置在“安全世界”中,允许内核通过一条特殊指令直接调用它,这是一种更高效、尽管仍然昂贵的转换。
这种“成本”是隐私的代价。安全保证不是免费的;它们是以性能为代价的。每当程序进入或退出飞地时,处理器都会进行一系列复杂且耗时的步骤。它必须保存和恢复状态,刷新像翻译后备缓冲器(TLB)这样的缓存,并预热内存加密引擎。单次进入就可能花费数微秒,这在处理器时间里简直是天长地久。
这种性能现实给不可信的操作系统带来了新的责任。考虑像 Intel SGX 这样的 TEE,它使用一个称为飞地页缓存(EPC)的特殊、有限的内存区域。如果操作系统天真地一次性调度太多的飞地运行,它们合并的内存占用可能会超过 EPC 的容量。结果就是“颠簸”(thrashing),系统把所有时间都花在换入换出飞地内存上,导致性能急剧下降。一个“TEE 友好”的调度器必须更聪明。尽管它看不到飞地里有什么,但它必须知道它们的工作集有多大。调度问题转变为一个经典的谜题:如何用最少的箱子(调度批次)将不同大小的物品(飞地工作集)装入一个固定大小的箱子(EPC)。操作系统必须解决这个装箱问题,才能成为一个有效、尽管不可信的系统资源管家。
一个飞地就像城堡中心的一个加固房间。墙壁很坚固,但如果攻击者能从外部挖隧道进来怎么办?在现代计算机中,外围设备——网卡、存储控制器、GPU——是强大的实体,它们可以通过一种称为直接内存访问(DMA)的机制直接写入内存。没有适当的防御,一个恶意设备可以轻易绕过 CPU 的保护,从外部破坏飞地的内存。
为了保卫整个堡垒,TEE 需要一个守门人。这个角色由输入输出内存管理单元(IOMMU)扮演,它是一个硬件部件,充当所有 DMA 流量的边境管制员。在设备传输数据之前,IOMMU 会检查其页表,看它是否有权限访问目标内存地址。为了给飞地启用安全的 I/O,操作系统或一个受信任的运行时必须用“默认拒绝”的策略来精心配置 IOMMU。它会创建一个精确的列表,指明某个特定设备被允许访问哪些内存页面——通常是一个小的共享缓冲区——并拒绝其他所有访问。这种配置是一项重要的任务,需要在 IOMMU 的内存结构中设置可能数千条映射规则,其复杂性随着设备和缓冲区的数量而增加。
这阐明了一个更深层次的原则:一个安全的系统是一条信任链。机密计算是其中的一个环节,但它必须与其他环节相连。像 UEFI 安全启动这样的技术创建了一个静态信任根(SRTM),从通电的那一刻起就验证每一段软件的加密签名。像 Intel TXT 或 AMD SKINIT 这样的技术创建了一个动态信任根(DRTM),允许系统在启动过程的后期启动一段纯净的、经过度量的代码(如虚拟机监视器),无论之前发生了什么。可信平台模块(TPM)将这些度量记录在特殊的寄存器(PCRs)中。通过检查 SRTM 寄存器,远程方可以验证固件的完整性;通过检查独立的 DRTM 寄存器,他们可以验证虚拟机监视器。整个技术栈,包括 TEE 和 IOMMU,必须协同工作,以提供一个整体的、可证明的、安全的平台。
机密计算的影响在云和分布式系统中最为深远。
在虚拟化的云环境中,客户的整个虚拟机(VM)只是云提供商服务器上的一个文件。当主机提供商可以随意快照、恢复或修改虚拟机的“虚拟”安全硬件,如虚拟 TPM(vTPM),虚拟机又如何能信任它呢?如果主机可以将 vTPM 恢复到先前的状态,它就能迫使虚拟机无休止地重复一个看似有效但实际上已经危险过时的“安全”启动过程——这就是“回滚攻击”。解决方案是将虚拟信任锚定在物理现实中。一种方法是让主机的物理 TPM 发布一份包含一个非易失性、严格递增的单调计数器的证明报告。当计数器值未能增加时,任何回滚尝试都会被远程验证者发现。一种更强的方法是在主机的硬件 TEE 内部运行整个 vTPM 本身,利用 CPU 自身的隔离能力来保护虚拟安全模块免受运行它的主机的侵害。
也许最令人兴奋的是,TEE 促成了一种从纯粹隔离到安全协作的转变。想象几个组织,比如多家医院,希望在不向彼此透露敏感数据的情况下,利用他们合并的患者数据来训练一个机器学习模型。他们可以使用机密计算。每家医院都可以在一个飞地内运行其计算部分。这些互不信任的飞地随后可以使用加密证明来建立一个安全的通信通道,并创建一个共享的加密状态。他们可以管理一个组加密密钥,并定期轮换以确保前向保密性,这样未来的妥协也不会泄露过去的数据。在这个模型中,没有任何一方——无论是医院还是云提供商——能看到原始数据,但他们都可以从集体计算中受益。
这个单一的想法——一个由硬件强制执行的小型可信环境——最初是为了保护应用程序免受其操作系统的侵害。然而,正如我们所见,它的影响无处不在。它改变了系统设计的哲学,重新定义了操作系统的架构,要求系统硬件进行新的协作,并最终在一个我们日益不信任的世界中为安全计算创造了全新的可能性。这是一个美妙的例子,展示了一个单一、强大的原则如何能够统一和照亮一个广阔而复杂的领域。