关于AWS Nitro Enclaves的几点说明:镜像与认证
AWS Nitro Enclaves是支持认证的锁定式虚拟机,属于可信执行环境(TEE),类似于Intel SGX,适用于运行高安全关键代码。然而,该平台缺乏全面文档和成熟工具链。因此,我们通过深入研究填补文档空白,重点发现安全陷阱并提供规避建议。
本文聚焦于enclave镜像和认证过程。以下是构建和签名enclave时避免安全陷阱的简要建议:
- 最小化构建镜像时的隐式信任关系
- 检查内核版本和哈希值
- 审查内核配置和启动命令行
- 验证预编译二进制文件(init可执行文件、NSM驱动和linuxkit工具)
- 确认使用正确的Docker镜像构建enclave镜像
- 从可信源获取AWS根证书并验证其哈希
- 确保系统威胁模型考虑AWS作为集中信任点的事实
- 除PCR-0外检查PCR-1和PCR-2
- 注意EIF的元数据部分未经认证
- 不要解析EIF签名——应重构并验证它们
- 不要将未保护的私钥推送到EC2实例进行enclave签名
- 不要对不可信EIF使用nitro-cli describe-eif命令
运行enclave
要运行enclave,需通过SSH连接AWS EC2实例并使用nitro-cli工具执行以下操作:
- 从Docker镜像和预编译文件构建enclave镜像
- Docker用于创建enclave用户空间文件的归档
- 预编译二进制文件将在后文详述
- 从enclave镜像启动enclave
enclave镜像采用enclave镜像文件(EIF)格式的二进制blob。
图1:构建enclave的流程
启动enclave时的底层操作:
- 从EC2实例释放内存和CPU并保留给enclave
- EIF复制到新保留的内存
- EC2实例请求Nitro Hypervisor启动enclave
- Nitro Hypervisor负责保护enclave(例如在内存返回EC2前清除)
- enclave附加到父EC2实例且无法在实例间移动
enclave内执行的所有代码均由EIF提供。
EIF格式
EIF格式的最佳"规范"是aws-nitro-enclaves-image-format仓库中的代码。格式简单:头部和节数组,每节包含头部和二进制blob。
图2:EIF的头部和节结构
CRC32校验和基于头部(减去4字节校验和保留位)和所有节(含头部)计算。
EIF节类型包括:
- Kernel:二进制bzImage文件
- Cmdline:内核启动命令行字符串
- Metadata:构建信息的JSON(如内核配置、Cargo和Docker版本)
- Ramdisk:cpio格式的引导ramfs(含NSM驱动和init文件)和用户空间ramfs(含Docker镜像文件)
- Signature:CBOR格式的元组向量(证书,签名)
EIF包含运行VM所需全部数据:内核镜像、命令行、引导二进制文件和用户空间文件系统。
信任谁?
在深入细节前,需了解EIF创建时涉及多个隐式信任关系。因此,验证数据如何进入EIF镜像至关重要。
验证EIF数据流需查看nitro-cli使用的enclave_build包。内核镜像(bzImage文件)、init可执行文件和NSM驱动预编译后存储在EC2实例的/usr/share/nitro_enclaves/blobs/
文件夹,在安装aws-nitro-enclaves-cli-devel包时拉取。
图3:Nitro Enclaves CLI安装文档部分
- 使用自定义配置下载并构建内核
- 用gpg2工具验证内核签名(信任torvalds@kernel.org和gregkh@kernel.org的密钥)
- 构建用于引导系统的init可执行文件
- 构建enclave与Nitro Hypervisor通信的NSM驱动
二进制文件也可在aws-nitro-enclaves-cli仓库找到。比较三个来源的SHA-384哈希(EC2实例、aws-nitro-enclaves-cli仓库和aws-nitro-enclaves-sdk-bootstrap构建):
组件 | EC2实例 | aws-nitro-enclaves-cli | aws-nitro-enclaves-sdk-bootstrap |
---|---|---|---|
Kernel | 127b32…9821c4 | 127b32…9821c4 | 4b3719…016c58 |
Kernel config | e9704c…7d9d35 | e9704c…7d9d35 | 9e634d…663f99 |
Cmdline | cefb92…ab0b0f | cefb92…ab0b0f | N/A |
init | 7680fd…a435bb | e23a90…4272ea | 601ec5…d4b25e |
NSM driver | 2357cb…8192c9 | 93d1f…657b50 | 96d0df…4f5306 |
linuxkit | 31ed3c…035664 | 581ddc…2ee024 | N/A |
内核源代码安全获取且哈希一致。手动构建内核哈希不同可能因配置差异。可手动验证内核配置和启动命令行,故其哈希不重要。
有趣的是,init和NSM驱动的哈希完全不符。为确保这些可执行文件未被恶意修改,需从源代码构建并调试新构建与预编译版本的差异(使用GDB或Ghidra),或信任预编译文件安全。
接下来是ramdisk节,即存储二进制文件的cpio归档。每个EIF至少有两个ramdisk:
- 第一个ramdisk含init可执行文件和NSM驱动
- init安装NSM驱动,chroot到rootfs/目录,并调用.cmd文件的execvpe(使用.env文件的环境变量)
- 第二个ramdisk从提供给nitro-cli的Docker镜像创建
- 存储init用于pivot的命令(.cmd文件)、环境变量(.env文件)和Docker镜像的所有文件(rootfs/目录)
- 命令和环境变量从Dockerfile解析
构建ramdisk的cpio归档时,nitro-cli使用linuxkit工具(与其他预编译文件一起下载)。AWS使用"稍加修改"的版本(故哈希不匹配)。linuxkit下载Docker镜像并提取文件,尝试制作完全相同的可复制副本。值得注意的是,nitro-cli使用过时的linuxkit 0.8版本。
图4:EIF创建过程示意图
nitro-cli获取用于构建EIF的Docker镜像的方式:
- 若提供
--docker-dir
命令行选项,则本地构建镜像 - 否则检查镜像是否本地可用
- 若不可用,则使用shiplift库和本地文件凭据拉取镜像
linuxkit也尝试使用本地可用镜像;若不可用,则通过docker login命令获取凭据从远程仓库拉取。
以可复制、透明且易审计的方式从Docker文件生成enclave很棘手——可阅读Artur Cygan的"增强SGX enclave信任"博文。构建EIF时,至少应确保nitro-cli使用正确镜像。为此,查阅Docker构建日志(因Docker镜像和守护进程不存储镜像来源信息)。
认证什么?
AWS Nitro Enclaves的主要特性是加密认证。运行中的enclave可请求Nitro Hypervisor计算(测量)enclave代码哈希并用AWS私钥签名(更准确地说,是用由证书签名的证书……最终由AWS根证书签名的证书签名)。
可使用加密认证特性在enclave源代码与实际执行代码间建立信任。只需确保从可信源获取AWS根证书并验证其哈希。
重要的是AWS同时拥有认证密钥和基础设施。这意味着必须完全信任AWS。若AWS被入侵或恶意行为,则游戏结束。此安全模型不同于SGX架构(信任分割 between Intel(认证密钥所有者)和云提供商)。
Hypervisor签名enclave哈希时,具体签名aws-nitro-enclaves-nsm-api仓库指定的CBOR编码文档。文档中有多项,但目前关注平台配置寄存器(PCR),即与enclave关联的测量(加密哈希)。前三个PCR是enclave代码的哈希。
图5:enclave的前三个PCR
PCR 0至2仅是节数据的SHA-384哈希:
- PCR-0: sha384(‘\0’*48 | sha384(Kernel | Cmdline | Ramdisk[:]))
- PCR-1: sha384(‘\0’*48 | sha384(Kernel | Cmdline | Ramdisk[0]))
- PCR-2: sha384(‘\0’*48 | sha384(Ramdisk[1:]))
可见,节数据间无域分离——节简单拼接。此外,PCR哈希不含节头部。这意味着可在相邻节间移动字节而不改变PCR。例如,若从第二个ramdisk开头剥离字节并附加到第一个,PCR-0测量不会改变。这是定时管道炸弹,但目前不可利用。无论如何,建议尽可能除PCR-0外检查PCR-1和PCR-2。
另一观察是EIF的元数据节未经认证。未指定用户如何及何时使用该节,故难以想象此属性的利用场景。只需确保系统安全不依赖该节内容。
在哪里签名?
最后讨论EIF的签名节。该节含CBOR编码的元组向量,每个元组是证书-签名对。签名是CBOR编码的COSE_Sign1结构,含编码有效负载(PCR索引-值对元组)、对有效负载的实际签名和一些元数据。证书为PEM格式。
|
|
当前EIF格式版本中,该节仅含PCR-0签名(整个enclave镜像的哈希)。(但注意可制作含多个签名元素的EIF;Hypervisor仍会运行,但不会验证第一个后的签名。)
签名代码由aws-nitro-enclaves-cose库实现。
PCR-8是EIF文件签名证书的哈希,计算如下:证书先从原始PEM格式解码并编码为DER。
|
|
如何验证签名?文档指示用户从COSE_Sign1对象解密有效负载以获取PCR索引-值对,并将PCR值与预期PCR比较。我们认为存在术语问题,其意应为验证实际签名,然后从有效负载提取PCR并与预期比较。然而,我们建议从预期PCR重构COSE_Sign1有效负载并验证签名。这应避免因无效解析遇到错误。(下一节讨论此类错误。)
官方签名enclave方式是在EC2实例上使用nitro-cli工具(图6)。这迫使将私钥推送到实例(图7)。这不是处理私钥的理想方式。更糟的是,AWS文档未指示用户用密码保护密钥……
但无任何阻止在EC2实例外运行nitro-cli,甚至可在离线环境运行。毕竟EIF只是一堆头部和二进制blob——构建和签名镜像不需要Nitro Hypervisor。AWS仓库甚至有在Docker容器中构建EIF的示例。此外,aws-nitro-enclaves-cli仓库有待合并的PR,将支持用KMS签名EIF。
图6:AWS文档称nitro-cli必须在EC2实例运行。
|
|
图7:私钥必须存储在本地文件。
总体建议不遵循AWS文档关于签名EIF的部分。以下是安全签名EIF的几种选项(按推荐顺序):
- 将私钥和Docker镜像推送到离线环境并在那里签名EIF
- 修改nitro-cli以启用更安全签名(使用HSM、KMS、keyring等)
- 等待支持KMS签名EIF的nitro-cli PR合并;这样无需自行修改nitro-cli
- 按AWS推荐将私钥推送到EC2实例并在那里签名EIF,但先用密码保护密钥(nitro-cli构建EIF时会询问密码)
如何解析?
既然知道enclave镜像的样子,接下来讨论如何解析。若熟悉文件格式解析器中的安全错误,可能已发现解析过程中的歧义和潜在问题。
有两个EIF解析器:
-
公共解析器:nitro-cli describe-eif命令
-
私有解析器:Nitro Hypervisor用于启动enclave
-
CRC32校验和是否验证?是。无效时enclave不启动。
-
EIF可有多于两个ramdisk节?是。所有ramdisk节简单拼接。
-
可截断(破坏)ramdisk节中的cpio归档?是!Hypervisor忽略某些cpio错误。
-
EIF可有多于一个内核或cmdline节?可能不行,但难确保不可能。
-
可交换不同类型的节(如将cmdline节放在内核节前)?是。这样做改变PCR-0测量。
-
EIF头部元数据中的节大小是否与节实际头部中的大小验证?是。
-
EIF节间可含数据?是。若含,CRC32校验和也计算该数据。
-
EIF头部的num_sections字段是否与节大小和节偏移数组中的项验证?否。num_sections后的项被忽略。
-
section_sizes数组中的大小是否含节头部?否。数组仅存储数据长度。
-
EIF签名节可有多于一个PCR索引-值元组?否。
-
EIF可使用空PCR索引-值向量?否。
-
可签名PCR-0外的PCR?复杂,但不行。PCR索引可为任意数据(甚至非数字),但值必须是PCR-0值。
-
EIF签名节可存储多于一个证书-签名对?是。
-
所有证书-签名对是否验证?否。仅验证第一对。
将以上发现与nitro-cli解析器代码比较,可见两解析器工作方式不同。或许最重要区别是nitro-cli解析器不尊重头部元数据(如num_sections和节偏移)。因此,nitro-cli解析器可能产生与Hypervisor解析器不同的测量。建议不要使用nitro-cli describe-eif命令获取不可信EIF的PCR。相反,从源构建EIF或运行它们并使用nitro-cli describe-enclaves命令(该命令向Hypervisor查询测量)。
为什么相关?
我们在TEE(如AWS Nitro Enclaves)中运行高安全关键代码,故必须正确处理细节。但AWS Nitro Enclaves文档严重缺乏,使理解细节困难。该特性还缺乏成熟工具链并包含多个安全陷阱。因此,若使用AWS Nitro Enclaves,务必遵循本文开头的清单!若需进一步指导,我们的AppSec团队定期举行办公时间。联系我们安排会议,向专家提问。
了解更多关于AWS的内容,查看Scott Arciszewski的博文"云加密揭秘:Amazon Web Services"和Joop van de Pol关于TEE特定问题的博文"翻转比特的踪迹"。
若喜欢本文,请分享: Twitter LinkedIn GitHub Mastodon Hacker News
页面内容 运行enclave EIF格式 信任谁? 认证什么? 在哪里签名? 如何解析? 为什么相关? 最近文章 我们构建了MCP始终需要的安全层 利用废弃硬件中的零日漏洞 Inside EthCC[8]:成为智能合约审计员 使用Vendetect大规模检测代码复制 构建安全消息传递很难:对Bitchat安全辩论的 nuanced 看法 © 2025 Trail of Bits. 使用Hugo和Mainroad主题生成。