SGX-MySQL是欧盟在Horizon计划中资助的SERECA(SECUREENCLAVESFORREACTIVECLOUDAPPLICATIONS)研究项目中的一个课题,这是MySQL的一个分区版本,在执行SQL查询时SGX-MySQL保证了数据机密性和结果完整性。SGX-MySQL依赖于SGXexclave中的一个小的可信内核来执行物理查询操作并产生查询结果,而其余代码的执行可能是在不受信任的环境下完成。SGX-MySQL的分区架构是由SGX分区框架半自动获得的,它由以下主要组件组成:
跟踪收集工具,用于确定用户定义的应用程序工作负载将访问敏感数据的部分
分区工具,基于跟踪收集到的轨迹以及用户定义的配置参数,提出应用程序的有效分区
代码生成工具,对C/C++应用程序的源代码进行转换,实现得到的分区
分区过程是半自动化的,因为它需要用户对敏感数据进行初始标记,并且还要完成根据用户需要配置分区工具的操作。
01跟踪收集
为了确定应用程序的哪些部分需要在Enclave内执行和存储,用户必须为跟踪收集工具提供那些敏感数据的初始定义。在MySQL示例中,这是通过将所有数据标记为敏感数据来完成的,这些数据作为用户表数据库页面的一部分从磁盘加载。由于该工具是使用二进制检测框架Valgrind实现的,初始标记是通过使用来自应用程序代码内的Valgrind客户端请求来完成的,用于指定包含敏感数据的内存范围,然后重新编译的应用程序在Valgrind中执行用户定义的工作负载。
该工具检测应用程序二进制文件集成了对内存中标记(或污染)的敏感数据的字节级跟踪。这样做允许该工具在整个被检查应用程序代码库中记录所有访问和敏感数据的传播。跟踪收集过程还记录有关函数依赖项、内存分配和执行的系统调用的信息。因此,该工具会生成许多带注释的调用树文件,其中包含分区工具确定有效分区所需的所有跟踪收集信息。
02分区
跟踪收集工具的输出与许多配置参数一起传递给分区工具,这些参数指定工具应如何考虑TCB大小和转换次数等因素。本质上,分区工具是将提供的调用树数据转换为整数线性规划(ILP)问题。该工具使用外部线性规划求解器Gurobi为定义的分区问题找到最优解,作为输出,它为用户提供了在执行跟踪收集期间已执行的所有代码的建议分区以及附加信息,例如调用堆栈,使用户能够了解为什么做出了特定的分区决定。分区工具的输出可以多种格式提供给用户,包括纯文本、HTML和DOT文件。
03代码生成
代码生成工具采用建议的分区并将其应用于代码库。它为需要Enclave转换的调用实现存根代码,并处理函数和方法。它是通过修改LLVM/Chang编译器工具链实现的。在安全MySQL实施的上下文中,该工具的输出将是MySQL服务器的修改版本,它可以部署在支持SGX的硬件上,并将在安全enclave内执行SQL查询。
04MySQL简述
MySQL是最流行的关系数据库管理系统(RDBMS)之一,作为具有大型代码库和复杂架构的以数据为中心的应用程序,MySQL是一个很好的应用程序的范例,它可以通过在IntelSGX之上运行来保护它存储和处理的数据,但面临的挑战是手动分区困难。
图1.1-1显示了MySQL架构的简化视图。客户端请求由连接管理器处理,该连接管理器重用连接池中的连接,或从线程管理器请求新的连接线程。MySQL为连接到它的每个客户端使用单独的连接线程。在对客户端进行身份验证后,将常规数据库查询分派给解析查询的解析器。查询优化器分析选择查询以找到执行它们的最佳策略。这包括决定哪些表需要按顺序扫描,哪个键查找更有效,表应该按什么顺序扫描,以及重写查询,例如用更有效的内连接替换外连接。一旦确定了策略,查询执行器负责执行生成的查询计划。
图1.1-1MySQL架构的简化视图MySQL架构的一个重要部分是存储引擎。存储引擎必须实现许多接口功能,例如用于顺序表扫描、键查找或表的创建和修改。在最新版本的MySQL中,仍然用于某些内部系统表的一个突出的存储引擎是MyISAM,这是一种非事务性磁盘支持引擎。当前版本中用户表的默认存储引擎是InnoDB。它支持事务和外键约束,不同之处还在于它可以在内存中缓存键值和常规数据,而MyISAM仅缓存键。通常相关的第三个存储引擎是内存或堆引擎。它不是磁盘支持的,而是将所有表数据保存在内存中。它的典型使用场景是临时表,这些表通常在某些查询的执行过程中使用,例如GROUPBY查询。
本项目专注于直接处理和保护敏感数据的组件。为此,主要考虑存储在用户表中的所有敏感数据。
05SGX分区框架设计
图1.2-1展示了整个分区过程和框架的两个主要组件,跟踪收集和分区工具。
图1.2-1分区框架的组件和分区过程第一步:敏感数据的初始标记
该框架采用半自动分区方法,该方法要求用户最初标记被认为是敏感的应通过在Enclave内处理来保护的数据,标记的位置对工具提出的分区有影响。理想情况下,初始标记是在敏感数据进入应用程序时完成的,要么是因为它是作为外部请求的一部分接收的数据,要么是因为它被应用程序本身加载到内存中,例如从磁盘读取数据时。在典型的SGX场景中,此数据将被加密,并且只有在将其传输到位于enclave中的应用程序部分后才会被解密。
对于MySQL,一个潜在的标记位置是InnoDB存储引擎。要将所有用户表数据标记为敏感,所有包含索引和实际表数据的用户表索引页一旦从磁盘加载到InnoDB缓冲池中,就可以被标记。一旦内存中的数据受到标记,跟踪收集工具会在将它复制到内存中的新位置时传播其标记,并根据标记信息记录对敏感数据的所有访问。
同样,客户端请求可用于清除数据标记以对其进行解密。这是必要的,因为敏感数据最终将离开enclave,例如作为对请求的响应的一部分发送给客户端或存储在磁盘上。在分区应用中,解密(离开Enclave)通常对应于数据的加密。一旦数据被加密,就不再需要通过保存在Enclave内来对其进行保护。
第二步:动态二进制检测
为了确定应用程序中访问敏感数据的部分,我们采用了动态二进制工具。应用程序使用Valgrind进行检测,以跟踪内存和CPU寄存器的负载和写入,并跟踪敏感数据如何在整个内存中传播。为此,每个加载、存储、放置和获取指令都进行了相应的检测。检测还用于记录所有函数的进入和退出,以及包含敏感数据的块的执行系统调用和内存分配。
第三步:执行和污点跟踪
一旦应用程序被检测,Valgrind将检测的代码转换回机器代码,然后执行它。由于采用动态而不是静态分析方法,因此最终分区取决于收集的执行跟踪和应用程序工作负载。因此,为了实现对给定应用场景有效的分区,应用必须在相应的用户提供的工作负载下执行。
在执行期间,通过将相关元数据存储在调用树数据结构中来跟踪所有函数调用。每个调用树节点都包含有关父节点和子节点的信息,它们分别对应于父函数和所有调用的函数。此外为每个节点记录它所代表的函数是否访问了敏感数据,是否调用了一个或多个系统调用,如果是,是哪些,以及函数分配的内存块。跟踪内存的分配位置很重要,因为不受信任的应用程序部分无法访问enclave内存,因此必须在虚拟地址空间的不信任部分分配需要从两个分区访问的内存。类似地,保存敏感数据的数据结构或对象通常应该在enclave内存中分配。
第四步:生成带注释的调用树
作为标记跟踪阶段的结果,Valgrind工具会生成一个或多个带注释的调用树文件,这样文件的数量取决于运行期间执行的线程数量,因为调用树是基于每个线程进行跟踪的。每个调用树文件包含每个调用树节点的以下数据:
函数名称
定义函数的文件和行号
父函数
调用次数
被调用的系统调用列表
一个布尔值,指定函数是否访问了受标记的数据
函数分配的内存块列表(用于代表malloc函数的节点)
执行期间启动的内存块列表(调用蕾丝malloc的函数时)
调用树的每个节点都代表一个函数的调用而不是一个函数本身,即一个函数可以根据它被调用的上下文而表现不同。此外,如果可以清楚地区分调用上下文,我们允许函数被复制并存储在不可信的环境中和Enclave分区中。
第五步:生成带注释的调用树执行跟踪和调用树分析
带注释的调用树文件用作分区工具的输入。它找到具有以下属性的所有调用树的分区:
所有访问敏感数据的功能节点都被分配到Enclave分区,所有执行系统调用的功能节点都被分配到不受信任的分区。
分配给enclave分区的节点数量最少,即分配给enclave分区的功能节点数量应尽可能少。
转换的总数最少,即转换的总数应该很小以减少性能开销。
不同的Enclave入口和出口点的数量最少,即两个分区之间不同的转换点的数量是相关的,因为它们中的每一个都需要手动检查以验证、加密和/或解密转换参数。因此,减少过渡点的数量不仅会减少应用程序分区所需的手动工作量,还会减少安全漏洞的风险。
一些分区要求可能相互冲突。TCB最小化可能会导致转换数量增加,相反,较少转换也许只能通过增加enclave分区中驻留的代码量来实现。因此,分区工具要求用户指定一个加权参数,该参数在TCB最小化与转换点的数量和转换总数之间被强调的比例程度。由于不同过渡点的数量和过渡的总数不一定是单调相关的,这意味着减少一个可能会增加另一个,用户还需要提供了一个加权参数来平衡这两个因素的影响。
为了找到最佳分区,将分区问题转换为整数线性规划问题,并使用外部求解器来解决它。所有调用树节点都表示为整数变量,表示它们被分配到哪个分区。类似地,所有边界都被转换为可以具有二选一的变量,如果边界代表两个分区之间的转换,则为1,如果没有转换,则为0。求解器找到最小值的目标函数定义为所有Enclave节点、所有转换和所有转换点的加权和的乘积。
第六步:生成分区结果
使用外部求解器解决IMP问题后,ILP结果将映射回实际代码库。如果表示功能的所有节点中的至少一个已分配给Enclave分区,则分区结果列出该功能作为Enclave的一部分。如果同时存在分配到不可信分区的节点和分配到enclave分区的节点,则相应的功能将被标记为必须复制。
此外,结果包含有关两个分区之间所有转换的信息,即所有进入enclave的入口点以及从enclave到不受信任分区的转换的所有出口点。所有这些转换都定义为一对函数,其中一个函数调用另一个函数。这里故意不允许这样一个函数对的多个实例,其中在一个实例中执行分区转换,但在另一个实例中不执行。虽然这在函数有时用于处理不敏感和敏感数据的情况下可能很有用,但在大多数情况下,很难甚至不可能在运行时决定需要调用哪个版本的函数。
第七步,代码生成
代码生成阶段使用在程序分析期间获得的分区规范(PS)生成应用程序的源代码级分区。它在enclave和外部代码之间拆分函数和变量定义,标识enclave接口,并为全局变量添加访问器函数。此外,它还强化了enclave接口,防止从外部代码接收到恶意输入,确保enclave维护敏感数据的机密性和完整性保证。此阶段的结果是一组包含enclave和外部源文件以及接口规范的目录,这些目录可以在支持SGX的CPU上编译和运行。
代码转换面临的主要挑战在于:
(a)处理进出enclave的调用
(b)在生成的enclave和非enclave版本中更改变量和函数的分配、范围和生命周期。
作为SGX分区框架的一部分,代码生成器依赖于LLVM/Clang编译器工具链重写预处理的C源代码,它使用Clang库将源代码解析为抽象语法树(AST),然后多次遍历AST以分析和修改源代码。除了Enclave和外部源文件之外,它还生成IntelSDK所需的Enclave定义语言(EDL)中的接口规范。
SGX分区框架向开发人员提供了接受函数指针参数的ecall和ocall及其调用站点的列表。然后,开发人员可以手动或使用静态函数指针分析,在枚举的调用站点识别这些函数指针的可能目标函数,最终完成半自动的代码转换。
总之,SGX分区框架会通过半自动转换MySQL应用程序的源代码,以便在Enclave的保护范围内仅执行应用程序的安全敏感部分,其中,一个基于动态分析,收集信息的跟踪收集工具,分区工具可以使用这些信息来生成应用程序的拆分,生成的拆分是在安全性与性能属性之间进行权衡,最后由代码生成工具完成转换应用程序的源代码以实现分区。