大家好!我是大聪明-PLUS!
本文将介绍嵌入式 Linux 安全性。
由于这个主题相当广泛,我们将简要介绍安全概念和威胁建模,然后重点介绍一些用于提高嵌入式 Linux 设备安全性的缓解技术,包括安全启动、代码/数据加密和安全密钥存储。
让我们首先从一些概念开始……
安全概念
安全的关键在于降低风险。
一方面,我们有所有者,即那些从产品或服务中受益的人(用户、制造商、企业主等)。所有者希望保护资产,即产品或服务中任何有价值的东西(数据、代码、声誉等)。
另一方面,我们有威胁行为者,即能够表现出威胁的人或事物(恶意黑客、政府等),任何能够以可能造成损害的方式对资产采取行动的事物。
为了表现出威胁,威胁行为者将通过攻击媒介(威胁行为者用来访问或渗透目标系统的方法或途径)探索漏洞(系统中的弱点) 。
归根结底,这是资产所有者和威胁者之间的一场猫捉老鼠游戏。资产所有者会为了保护资产付出多大代价?威胁者又会为了破坏资产付出多大代价?这实际上取决于资产的价值。事实上,资产所有者和威胁者对价值的认知可能并不相同。
可以通过称为威胁建模的过程来识别资产(及其价值)以减轻受到损害的风险。
威胁建模
威胁建模是一个识别、列举潜在威胁并确定缓解措施优先级的过程。它本质上是一个风险评估过程,用于评估资产的价值及其保护成本。威胁建模的结果就是产品的威胁模型。
有几种技术和方法可以在威胁建模期间提供帮助,包括 STRIDE、DREAD、VAST、OCTAVE 等。
为了对这个主题有一个非常基本的介绍,我们来谈谈 STRIDE 和 DREAD。
STRIDE模型是一个非常有用的威胁分类工具。它由微软开发,其名称是六种主要威胁类型的首字母缩写:欺骗、篡改、否认、信息泄露、拒绝服务和特权升级。STRIDE 可用于识别系统资产可能面临的所有威胁。
DREAD方法是一种评估计算机安全威胁风险的工具。其名称是五类安全威胁的首字母缩写:D损害(攻击的严重程度)、R可重复性(重现攻击的难易程度)、E可利用性(发起攻击的工作量)、A受影响用户(受影响人数)和D可发现性(发现威胁的难易程度)。
STRIDE 模型有助于识别威胁,而 DREAD 方法则有助于对威胁进行排序。对于系统中的每个威胁,您需要仔细检查每个威胁类别,并将其分为低(1 分)、中(2 分)或高(3 分)。最终,您将获得一个威胁和缓解策略的排序列表。
威胁建模能够清晰地展现我们想要保护的内容、保护计划以及相关成本。这是产品威胁模型的一部分,需要在每个开发周期重新评估。因此,威胁模型将提供一份按优先级排序的威胁列表,以便我们能够专注于实施缓解措施,从而提高产品的安全性。
如何保护代码的完整性和真实性?如何确保数据的隐私性?密钥应该存储在哪里?如何最大限度地降低应用程序被利用的风险?让我们尝试解答所有这些问题,甚至更多,从安全启动开始!
安全启动
如何确保你运行的代码是由值得信赖的人或公司编写的?实现安全启动过程。
安全启动过程的目标是保护代码的完整性和真实性。
安全启动通常基于数字签名的验证。嵌入式 Linux 系统通常包含三个主要组件:引导加载程序、内核和根文件系统 (rootfs)。所有这些组件都经过签名,并在启动过程中进行签名检查。
例如,可以使用某种硬件机制来检查引导加载程序的签名,引导加载程序会检查内核的签名,内核会使用 ramdisk 映像来检查根文件系统的签名。由于引导链中有一个组件会检查下一个组件的签名,因此这个过程通常被称为信任链。
让我们看一下NXP iMX6设备上的真实示例。
一切都始于 SoC 内部的 ROM 代码。在 NXP iMX6 上,有一个称为高可靠启动 (HAB) 的硬件组件,它能够验证第一阶段引导加载程序的签名,从而实现安全的启动过程。iMX6 设备内的高可靠启动也可以称为“信任根”,因为如果它被攻破,所有安全启动过程也会受到影响。
iMX6 SoC 内部的 ROM 代码将使用 HAB 组件检查引导加载程序的签名。为此,需要生成一对密钥(公钥和私钥),使用私钥对引导加载程序进行签名,并将公钥存储在 SoC 内部。在 iMX6 上,使用 OTP 熔丝来存储密钥。实际上,为了降低成本,SoC 中仅存储公钥的哈希值。
当引导加载程序(例如U-Boot)启动时,它必须检查 Linux 内核的签名。为此,通常使用一种名为FIT 镜像的镜像格式。FIT 镜像是一个包含多个二进制文件的容器,支持哈希和签名,通常包含 Linux 内核镜像、设备树文件和一个初始 ramdisk。生成一对密钥后,我们需要使用私钥对 FIT 镜像中的二进制文件进行签名,并配置 U-Boot 使用公钥检查 FIT 镜像的签名。
内核启动后,它将从 ramdisk 映像运行init程序。ramdisk 会自行验证最终根文件系统的完整性,然后再挂载。实现此验证的逻辑有很多种。一种常见的方法是使用 device-mapper verity 内核模块。dm -verity内核模块提供块设备的完整性检查,并且需要只读的根文件系统(squashfs 是一个不错的解决方案)。如果您需要读写根文件系统,其他方法包括IMA或dm-integrity。
这只是安全启动实现的一个示例,尽管它可以应用于不同的电路板和 ARM SoC。
但没有任何东西是 100% 安全的!
2017年7月17日,多款恩智浦设备(i.MX6、i.MX50、i.MX53、i.MX7、i.MX28 和 Vybrid 系列)的 ROM 代码中存在安全启动漏洞,这些漏洞已被公开披露。一旦您的信任链被破坏,一切就都危在旦夕了!因此,我们需要警惕此类漏洞(本例中,这些漏洞已在新芯片中得到修复)。
安全启动虽然可以确保真实性和完整性,但它无法保护设备免遭伪造,也无法防止威胁行为者从设备中提取代码/数据。因此,如果您想保护自己的知识产权或确保数据的机密性,就需要使用加密技术。
代码和数据加密
您可能想要加密嵌入式 Linux 设备中的数据或代码。
当您需要保护用户的隐私和机密性时,数据加密是一种常见的方法。数据是指设备执行期间生成的任何信息,包括数据库、配置文件等。
代码加密视具体情况而定,加密整个根文件系统并不常见。通常,大多数组件都是免费开源软件,因此没有什么可隐瞒的。此外,还存在 GPLv3 和 Tivoization 的问题(使用任何 GPLv3 软件都会强制要求用户提供软件更新机制,如果您对软件进行加密,这会使更新更加困难)。更常见的用例是仅加密您为设备开发的应用程序。这通常是您的知识产权所在。
Linux 中的加密基本上有两种主要方法:全盘加密和基于文件的加密。
全盘加密提供块级加密,可对整个磁盘或磁盘分区进行加密。为此,我们可以使用Linux 内核的设备映射器加密目标dm-crypt。
基于文件的加密 (FEP) 在文件系统级别提供加密,其中每个目录可以单独加密,并可选择使用不同的密钥进行加密。两种最常见的基于文件的加密实现是fscrypt和CryptFs。 fscrypt 是一些文件系统(例如 EXT4、UBIFS 和 F2FS)上提供的 API,而 eCryptFS 是一种更通用的解决方案,以堆叠在现有文件系统之上的层的形式实现。
但是用于加密的密钥又如何呢?
加密密钥
由于非对称密钥算法速度太慢,无法用于加密,因此通常使用对称密钥算法进行加密。这意味着加密和解密使用相同的密钥,并且该密钥应该位于文件系统中的某个位置,以便可以解密加密的代码/数据。
但是我们不能把密钥留在文件系统中,对吧?
有不少公司对此漠不关心,最终付出了代价。例如,第一代 Xbox 视频游戏机的截图。安全研究员开发了一种专门的 FPGA 硬件来嗅探通信总线并从设备中提取加密密钥。这仅仅是因为密钥就在那里,以明文形式在通信总线中传输,没有任何保护措施(顺便说一句,Andrew Huang 是一本关于硬件黑客的优秀书籍《Hacking the Xbox》的作者,你可以在他的网站上免费下载)。
结论是,加密代码/数据的保护与解密密钥的保护一样安全!
因此加密带来的一个挑战就是密钥存储。
关键存储技术
在桌面电脑或智能手机上,用于加密文件系统的密钥可以通过交互输入的用户密码(passphrase)获取。例如,在 Android 智能手机上就是这样运作的。
在嵌入式系统上,通常我们不需要每次启动设备时都与用户交互来从密码中获取密钥。因此,密钥应该以加密形式存储在文件系统或安全存储中。我们需要硬件支持来实现这一点。
例如,恩智浦 i.MX 处理器拥有一个唯一的主密钥(由恩智浦预先编程),只能由一个名为CAAM(加密加速器和保证模块)的特殊硬件模块访问。该硬件设备可用于加密密钥并将其存储在文件系统中(这必须在制造过程中完成)。在启动过程中,CAMM 模块将用于解密密钥并恢复用于解密文件系统的纯密钥。由于 CAAM 模块内的密钥无法访问,因此加密密钥受到保护。
如果处理器本身不具备安全功能,您可以使用外部硬件(例如安全元件或TPM 设备)来实现相同的效果。这些外部设备通常提供安全存储,因此可以用来存储主密钥,该主密钥可用于加密/解密文件系统加密密钥。这些设备还提供许多安全功能,例如随机数生成、哈希计算、加密和签名功能等。
安全元件是一种安全的计算系统。它本质上是一种安全存储,拥有自己的安全应用程序(通常使用 Java Card 实现,但并非必需)。安全元件的功能非常开放,并且取决于具体实现,但大多数安全元件都实现了公钥加密标准 11 。智能卡和 SIM 卡就是安全元件的例子。
TPM (可信平台模块)是一项规范和国际标准(ISO/IEC 11889)。TPM 并非安全元件,尽管它可以在安全元件内部实现。它可以采用硬件或软件方式实现,但大多数情况下采用硬件方式。它提供了一组由标准定义的有限安全功能,包括安全存储和加密功能。
安全存储的第三个替代方案是使用可信执行环境(TEE)。在可信执行环境 (TEE) 中,代码执行和数据访问在机密性(无人可访问数据)和完整性(无人可更改代码及其行为)方面受到隔离和保护。我们周围的许多设备都使用可信执行环境,包括智能手机、机顶盒、视频游戏机和智能电视。
最后,如果您关心嵌入式 Linux 设备中的加密,则必须从项目开始就考虑密钥存储和管理。
分层安全
到目前为止,我们已经讨论了安全启动和加密如何提升嵌入式 Linux 设备的安全性。但这还不够。我们应该始终分层思考安全性,每一层都应用一种构建在其他层之上的缓解技术来提升设备的安全性。而多种缓解技术的结合,可以增加攻击者入侵设备的难度。
想想看。您可以使用安全启动和加密来保护您的代码和数据,但如果您运行的应用程序存在可能被利用的漏洞,您的资产仍然面临风险。如果应用程序存在攻击向量(用户输入、配置文件、网络通信等),漏洞就可能被用来攻击该应用程序。尤其是在使用内存不安全的语言(例如 C 和 C++)编写的程序中,缓冲区溢出等漏洞可能会被用于堆栈破坏和格式化字符串等攻击。
因此,未来还会有更多缓解技术。