软件堆栈的这些最低层在安全性方面的主要作用是提供安全域的隔离和可能违反隔离的所有操作的中介。在理想情况下,操作系统将任何单个进程与所有其他进程隔离开来。例如,外围进程应该无法访问分配给主进程的内存,了解与该主进程相关的活动的任何信息,除了进程选择显示或阻止进程无限期使用其分配的资源(如CPU时间)的那些。某些操作系统甚至可能调节信息流,以便在没有适当许可的情况下永远不会将绝密数据泄漏到进程,或者分类数据不能由没有适当权限级别的进程修改。
深入挖掘,我们可以区分控制平面和数据平面操作,我们看到操作系统中的隔离涉及两者。在内存隔离中,操作系统在配置MMU(内存管理单元)时在控制平面上运行,然后负责隔离,操作系统不会过多参与。在大多数其他交互中,例如,在对非特权安全域提供的系统调用参数进行操作时,操作系统在两个平面上运行。平面之间缺乏分离很容易导致漏洞,例如,当操作系统决定在另一个域中重用以前属于一个安全域(由MMU强制实施访问隔离)的内存页时,没有正确覆盖(可能敏感)数据。
设计操作系统的方法有很多种。无花果。1说明了四种极端设计选择。在图内。1(a),操作系统和应用程序在单个安全域中运行,并且没有任何隔离。早期的操作系统是这样工作的,但今天的许多嵌入式系统也是如此。在这种情况下,系统中的不同组件之间几乎没有隔离,应用程序可能会损坏文件系统(FS)的活动,网络堆栈、驱动程序或系统的任何其他组件。
图1(b)显示了大多数现代通用操作系统的配置,其中大多数操作系统驻留在单个安全域中,与应用程序严格隔离,而每个应用程序也与所有其他应用程序隔离。例如,这是Windows,Linux,OSX和原始UNIX的许多后代的结构。由于操作系统的几乎每个组件都在单个安全域中运行,因此该模型非常有效,因为这些组件仅通过函数调用和共享进行交互记忆。只要每个组件都是良性的,该模型也是安全的。但是,如果攻击者设法破坏单个组件(例如驱动程序),则所有安全性都将无效。通常,设备驱动程序和其他操作系统扩展(例如,Linux内核模块)是系统安全性的重要考虑因素。通常由第三方编写,并且比核心操作系统代码更有缺陷,在操作系统的单个安全域中运行的扩展可能会完全损害系统的安全性。
有趣的是,内核和此类系统中其他安全域之间的边界通常有点模糊,因为操作系统可以绕过内核进行高速网络,或者将非性能关键型操作系统组件实现为用户进程。示例包括UNIX操作系统中的用户空间文件系统(FUSE)和Windows中的用户模式驱动程序框架(UMDF)。即便如此,大多数操作系统功能仍然形成单个整体式安全域。
图1(c)显示了多服务器操作系统中构成操作系统的所有组件在单独进程中的极端分解。该配置的效率可能低于以前的模型,因为操作系统不同组件之间的所有交互都涉及进程间通信(工控机)。此外,操作系统作为分布式系统运行,任何曾经构建过分布式系统的人都知道问题可能会变得多么复杂。但是,多服务器系统的优点是,例如,受感染的驱动程序不能轻易危害系统的其余部分。此外,虽然从概念的角度来看,多服务器看起来像一个分布式系统,但真正的分布式系统的很多复杂性是由于不负责任的通信,这在多服务器系统中是不存在的。普遍的观点是,基于微内核的多服务器设计比单片和单域设计具有安全性和可靠性优势,但会产生更高的开销-安全价格。
最后,图1(d)显示的情况乍一看与图相似。1(a):在多路复用底层资源的最小内核之上,应用程序与最小的“库操作系统”(libOS)一起运行。libOS包含的代码通常是操作系统的一部分,但直接包含在应用程序中。此配置允许应用程序完全根据其需求定制操作系统,并省略它们无论如何都不打算使用的所有功能。库操作系统最早是在1990年代提出的(例如,在麻省理工学院的Exokernel和剑桥的Nemesis项目中)。在相对默默无闻的几年之后,它们再次变得流行起来——尤其是在虚拟化环境中,它们通常被称为Unikernels。在安全性方面,Unikernels很难与基于微内核的多服务器系统相提并论。一方面,它们没有操作系统组件的极端分离。另一方面,它们允许(库)操作系统代码更小、更简单——它只需要满足这一个应用程序的需求。此外,该库不能妥协隔离:它是此应用程序可信计算基础的一部分,而不是其他库。
关于哪种设计更好的争论可以追溯到安德鲁·1992年,Tanenbaum和LinusTorvalds。到那时,MINIX,一个由Tanenbaum开发的小型类UNIX操作系统,已经存在了五年左右,并且作为教育操作系统在世界范围内获得了牵引力-特别是因为贝尔实验室的原始UNIX作为商业产品出售,并带有限制性许可证,禁止用户修改它。MINIX的用户之一是Torvalds,当时是一名芬兰学生,他在Usenet的comp.os.minix新闻组中宣布了新的操作系统内核。1992年1月,Tanenbaum批评该设计缺乏可移植性,并瞄准了Linux的整体设计,声称Linux从一开始就已经过时了。Torvalds回应了他自己对MINIX的批评。这场激烈的交流包含了越来越复杂的论点,其中许多论点今天仍然存在,以至于谁赢得了辩论的问题仍然没有答案。
图1:操作系统的极端设计选择:(a)单域(有时用于嵌入式系统),(b)单片操作系统(Linux、Windows和许多其他操作系统),(c)基于微内核的多服务器操作系统(例如,Minix-3)和(d)单内核/库操作系统
也就是说,Linux已经变得非常流行,很少有人会认为它已经过时了。同样明显的是,来自多服务器系统(如MINIX)的想法已被纳入现有的操作系统和基于虚拟机管理程序的系统中。有趣的是,在撰写本文时,甚至MINIX本身也在数亿个英特尔处理器中作为微型操作系统运行在称为管理引擎的独立微处理器上。此外,现在现代系统中的CPU越来越复杂。片上系统(SoC),硬件本身开始看起来像一个分布式系统,一些研究人员明确主张设计操作系统。因此,重点是消息传递,而不是用于通信的内存共享。
一般来说,虚拟化环境的情况与操作系统的情况相当。我们已经看到,在一种极端情况下,带有应用程序和精简操作系统的整个虚拟机可以形成一个域。更常见的情况是,在虚拟机中有一个最低级别的虚拟机管理程序,支持一个或多个操作系统,如Linux或Windows。换句话说,这些虚拟机管理程序为每个操作系统提供了一种在专用硬件上运行的错觉。在光谱的另一端,我们发现整个系统分解为单独的、相对较小的虚拟机。事实上,一些操作系统,如QubesOS,通过允许单个用户进程在自己的虚拟机中隔离,完全集成了虚拟化和操作系统的概念。最后,正如我们已经看到的,Unikernels在虚拟机管理程序之上的虚拟化环境中很受欢迎。
顺便说一句,虚拟机的缺点之一是每个操作系统映像都使用存储并添加冗余,因为每个系统都会认为自己是硬件山中的王者,而实际上它是共享的资源。此外,虚拟机中的每个操作系统都需要单独的维护:更新、配置、测试等。因此,一种流行的替代方案是在操作系统级别进行虚拟化。在这种方法中,多个环境(称为容器)在单个共享操作系统上运行。容器尽可能彼此隔离,并具有自己的内核命名空间、资源限制等,但最终共享底层操作系统内核,通常是二进制文件和库。与虚拟机相比,容器更轻量级。但是,如果我们暂时忽略管理方面,虚拟机通常被认为比容器更安全,因为它们非常严格地划分资源,并且仅将虚拟机管理程序共享为之间的薄层。硬件和软件。另一方面,有些人认为容器比虚拟机更安全,因为它们非常轻量级,我们可以将应用程序分解为“微服务”,在容器中具有明确定义的接口。此外,需要保护安全的东西更少,总体上减少了攻击面。容器(或“操作系统级虚拟化”)的早期工作可以在chroot调用中找到,该调用于1979年首次添加到版本7Unix。2000年,FreeBSD发布了Jails,它在操作系统虚拟化方面走得更远。今天,我们有许多容器实现。一个流行的是Docker。
最后一类操作系统明确针对小型和资源受限的设备,例如物联网(IoT)中的设备。虽然每个人对物联网的含义以及要考虑的设备范围从智能手机到智能灰尘都有不同的看法,但有一个共识,即大多数资源受限的设备应该是其中的一部分。对于此类设备,即使精简的通用操作系统也可能过于庞大,并且操作系统预计只能运行几千字节。举一个极端的例子,流行的物联网操作系统(如RIOT)的大小可能小于10KB,并且可以在从8位微控制器到通用32位CPU的系统上运行,带或不带内存管理单元(MMU)等高级功能。我们要求Windows和Linux等操作系统提供的丰富功能和应用程序隔离在这些操作系统中可能不存在,但可能支持实时计划或低功耗等功能网络在许多嵌入式系统中都很重要。
由于我们对操作系统提供的安全保证感兴趣,因此我们将假设存在多个安全域。在下一节中,我们将从既定的安全原则的角度阐述不同设计的优缺点。我们的重点将放在设计的安全性以及我们可以阻止攻击的方式上,但在观察到在这个级别上还有更多安全之前。特别是,系统的管理和维护性(在更新、扩展、配置等方面)起着重要作用。