随着物联网(IoT)装置的广泛普及——从智能城市到无线珠宝, 物联网几乎渗透到日常生活的每一步, 对于物联网类型的嵌入式系统划定安全优先顺序的需求日益迫切. 确保安全的启动过程是保护任何嵌入式系统的首要步骤, 也是在应用中预防恶意软件壁垒的必要部份. 让我们看看其优缺点, 并以电子产业中常见的处理器之一——i.MX6为例加以说明.
什么是安全启动?
'安全启动' (secure boot)是指作业系统(OS)在启动镜像与程式码之前必须先根据硬件进行认证, 才能使其用于启动的过程. 硬件必须以此方式提前作好准备: 它只认证使用受信任的安全凭证所产生的程式码. 总之, 它确保开机启动和OS软件是预期的制造商版本, 而不至于被恶意软件或恶意的第三方篡改过.
安全启动适用于任何单一用途的装置, 例如广泛使用i.MX6处理器(如整合E-Ink显示控制器的i.MX6 Solo和DuaLite)的电子书阅读器(e-reader), 它们主要用于阅读电子书, 而不是一般的运算. 在这种案例中, 在开机启动时锁定Linux环境是很有帮助的.
诸如Android手机等其它情形可能就有所取舍了, 例如, 使用安全启动可能会限制终端用户执行客制ROM. 能够做到这点也许是一项特色功能, 或者可能是根据产品布局或安全要求的理想功能. 基本上, 适于使用安全启动的理想时机在于, 当你不希望另一方在你的装置中载入作业系统或其它启动载入程式之际.
对于执行Linux的IP相机等更高整合度的系统来说, 更多人会建议你使用安全启动, 因为任何恶意开机程式码或作业系统软件, 都可能把你的装置变成僵尸网络的一部份. 或者可能是从摄影机拍到的画面被公开上传到网际网络, 或甚至被修改成不包含视讯主人想要的影片片段等资讯.
i.MX6上的安全启动过程
一旦在i.MX6上建立了开机启动镜像, 为了利用安全启动, 必须针对为此目的产生的SSL凭证产生一组安全金钥.
这些金钥用于产生一组安全指令, 然后利用供应商(如飞思卡尔, 如今的恩智浦)提供的工具编译后追加到启动镜像中. 处理器随即取得第一阶段的启动载入程式, 并采用你的凭证来认证以安全启动编译工具产生的凭证资料.
在写入启动媒介时, 如果启动镜像中的金钥资料符合储存在处理器安全存储器中的金钥资料, 将会开始执行安全指令, 然后检查镜像的密码杂凑值, 确保其与安全指示值相符. 如果彼此符合, 处理器将会开始载入并执行你的启动镜像.
一旦这个过程通过CPU的内部启动载入程式, 你仍然可以从启动载入程式的程式码调用安全开机库. 这可让你载入作业系统镜像, 并以CPU启动载入程式认证软件启动载入程式的相同方式加以认证.
在这个过程结束时, 作业系统将在通过验证的安全环境中完成启动. 你知道这很合理, 因为每一阶段都对于处理器中储存的金钥杂凑值进行认证测试.
一次性可编程设计
从安全角度看这个过程, 从SSL凭证所产生的根金钥(root key)经过杂散后, 再以一次性可编程(OTP)设计烧入CPU中. 一旦这个金钥烧入处理器后就不能修改了——理由之一是安全.
启动镜像也在此金钥基础上签名, 而在此签名过程所产生的资料将与镜像结合. 处理器利用其金钥检查你的镜像金钥, 如果彼此相符, 则会以刚好符合处理器的金钥检查你的镜像. 如果仍然相符, 就能执行镜像. 这一步骤会将你带到更上层链, 从CPU启动载入程式到正常启动载入程式, 再到作业系统.
当然, 这可能是i.MX6特有的情况, 实际上还有各种不同类型的安全启动, 例如采用UEFI安全启动的X86, 但为了便于理解, 本文重点还是放在i.MX6上.
利用硬件
i.MX6硬件套件包括许多有益于安全启动的特殊安全机制. 用于安全启动的关键部份是烧入金钥时所使用的一次性熔丝(one-time fuse). 一旦熔断后就无法再接起来, 因此一旦烧入你的金钥后, 其杂凑值就具有永久性了. 多个金钥也可以整合于一个金钥杂凑值, 因此如果受到威胁时就可以废除一个金钥.
系统安全性的另一个功能是CPU内部启动载入程式, 这是一段同样经过安全测试的静态程式码. 这是一直到作业系统的整条链得以保持安全的重要基础.
此外, i.MX6具有一个硬件密码演算法加速器. 诸如AES杂凑, Triple DES杂凑, SHA1和SHA256等演算法, 都能经由i.MX6处理器进行加速, 从而大幅提高安全处理的速度.
缺点
最明显的缺点是必须负责自己的安全性. 如果你的金钥泄露给外界, 就会有人利用储存在处理器中的金钥为程式码签名, 因此你必须确保处理过程和硬件的安全.
另外, 只为安全启动目的而配置的处理器, 只有在镜像得到正确签名后才能启动. 因此, 在将杂凑值烧入处理器的过程中, 如果发生任何差错都会导致处理器无法执行程式码, 因为烧入的杂凑值并不相符——因而成为一个无用的处理器.
一旦处理器被设置为安全执行, 那就必须载入安全的程式码. 它只允许你从储存(如SD卡或NAND快闪记忆体)中安全地载入, 或以其它方式将软件载入处理器(USB镜像载入)中.
因此, 硬件和处理器的准备工作一定要保证安全. 此外, 还必须确保你的启动载入程式也为此作好充份准备.
如前所述, 你的启动载入程式必须调用处理器上的安全启动库, 以便认证启动链的下一阶段. 如果未能正确地编码启动载入程式以便正确使用安全库, 那么你可能无法完全确保作业系统的安全性.
不要掉入虚假的安全感陷阱
需要弄清楚的是, i.MX6的安全启动并不会锁定整个系统, 而只是锁定作业系统软件. 因此有人可能编写一些在作业系统上执行的Linux恶意软件, 如果它们成功载入后, 将会危害到整个系统.
i.MX6安全启动认证
如果要求更完整的安全性, 还可以认证档案系统中的其他部份和程式码. i.MX6的安全启动过程原则是特定记忆体块具有特定的密码杂凑值和有关的签名资讯. 如此就可以将作业系统的根档案系统和其它重要档案载入记忆体中的某个固定位置, 同时载入正确的安全指令集. 这样允许你在必要时认证系统的其它部份.
i.MX6安全启动的重要诀窍
1. 确保启动过程是安全的 一旦你决定需要走安全启动路径, 那就必须确保相关过程也能并驾齐驱. 在生产环境中泄露了金钥将功败垂成.
2. 确保强劲的加密方法 确保所使用的加密方法足够强劲. 使用者很可能建立力度较弱的金钥, 而i.MX6上的安全启动也支援一些较老旧或如今被视为妥协折衷的组合. 因此, 确保你的演算法是最新的, 能够满足目标要求.
3. 检查你的程式码 为了实现安全启动, 意味着任何事情, 包括启动载入程式中的其余程式码, 作业系统和其它软件都必须在安全启动的原则下正确编写, 而不能出现安全漏洞.
另外, 启动过程的每一阶段在执行前必须检查下一步. 如果没有做到这点, 或者部份做到, 那么能够称为过程安全的范围就会小很多.
4. 认证每个地方 为了实现真正的安全, 尽可能地认证你想载入的所有程式码, 并确保遵循针对库建立的操作方法.
你必须确保整个过程的安全, 即如何产生和储存你的金钥. 安全启动只能检查签名, 而任何签名的镜像都会被处理器认为是安全的.
因此, 确保你所写入的每一个程式码独立部份, 都能被调用于处理器的安全启动库中, 以便继续认证你的镜像, 因为大多数i.MX6电路板都有多个阶段的启动过程, CPU内部启动载入程式先是载入SPL, 然后SPL接着载入完整的启动载入程式, 以便载入作业系统. 每一步都必须经过前一步骤的认证, 以便确认是安全的.
5. 确认过程经过有效认证 确保程式码能真正执行安全启动至关重要. 即使是安全的程式码也可能跳到记忆体的任何位置继续执行, 因为处理器的工作原理就是这样的. 为了保持安全, 实际确保程式码将会认证下一步程式码至关重要.
i.MX6最常使用的启动载入程式是U-Boot, 它真正支援在i.MX6上执行安全启动. 它需要进行配置, 不过比较简单些. 当有大量的工作已经先行处理好了, 你应该就会较少出错. 从头开始编写安全性并不是个好办法, 最好是采取一种已知的理想建置方式, 并使其符合你的设计需求.