如今,当谈到嵌入式安全性时,我们都有点不安全。这是一个快速发展的领域,新的威胁不断涌现。最糟糕的是,我们在嵌入式软件开发中通常生活在资源受限的硬件世界中,这与大多数嵌入式安全防御机制不一致。
更复杂的是,根据需要保护的内容的重要性、潜在的攻击面以及技术上和经济上的实用性,嵌入式安全需求会有很大的不同。换句话说,嵌入式安全更多的是一个范围,而不是一种一刀切的方法。例如,银行的安全要求与餐馆的安全要求有很大不同。嵌入式系统也是如此。
嵌入式系统的硬件安全
嵌入式处理器提供多种嵌入式安全特性。我们来看看最常见的。
l 防篡改。此功能保护固件免受未经授权的访问。这用于保护固件IP,并防止对手研究目标代码的漏洞。
l 锁定步骤执行。这种安全特性使用多个处理器来执行具有相同数据的相同代码,从而保证代码的准确执行。这种硬件主要出现在安全关键的应用程序中,更安全的代码通常更安全。
l 防故障。该安全特征采用电路来防止攻击者通过操纵电源或其他系统信号来导致异常程序执行。否则,攻击者可能能够“跳过”启用MMU或MPU设置的指令。
l 仅从闪存执行。大多数微控制器(MCU)执行来自闪存的指令。其中一些MCU还可以禁止从RAM执行,建议这样做有助于防止在远程执行攻击中动态插入恶意代码。
l 硬件堆栈限制。有些处理器有一个堆栈限制寄存器,可以防止堆栈溢出造成的内存损坏。
l 硬件看门狗定时器。许多处理器都有一个不可屏蔽的硬件看门狗定时器。这一安全特征起到自动防故障的作用。在正常操作下,应用程序代码定期复位看门狗,并且总是在它到期之前。在异常执行期间,看门狗不会复位,因此会产生一个不可屏蔽的中断来中止异常执行。通常,应用程序会在看门狗到期后简单地复位。
l 真随机数生成器(TRNG)。在硬件中拥有一个TRNG或者更基本的随机数发生器(RNG)是非常有益的。这对于联网的嵌入式设备非常重要。
l 内存管理单元(MMU)。通常,这种硬件安全特性只能在更大、更强大的处理器上使用,它可以限制对不同内存区域的访问。这通常由应用固件在复位后设置一次,以映射和保护对各种存储器区域的访问。
l 存储器保护单元(MPU)。这种硬件安全功能类似于MMU,在更小、资源更有限的设备中可以找到。同样,这通常由应用固件在复位后设置,以保护各种存储区域。在没有堆栈限制寄存器的情况下,MPU可以在每个线程的堆栈顶部设置一个保护块,以防止堆栈溢出。
l 安全元素(SE)或可信平台模块(TPM)。对于联网的嵌入式设备,SE或TMP可以安全地将凭证或其他机密与主应用程序隔离开来。这可以大大提高设备的网络安全性,因此强烈建议这样做。
l 当然,提到的每个硬件安全特性都有相关的成本——在电路、功耗、尺寸等方面。这些成本必须经过风险收益分析。
嵌入式设备的软件安全性
不管你拥有什么样的硬件安全特性,你都可以在软件中做一些事情来帮助提高嵌入式安全性。以下是一些可以提高设备安全性的软件最佳实践。
l 强化设备固件。你应该争取100%的语句和100%的分支/决策覆盖测试,因为更安全的代码也更安全。
l 利用静态分析和相关工具。除了加固之外,利用静态分析工具以及渗透测试和模糊化工具也是一个好主意。这些安全测试工具有助于发现开发中的微妙问题,这比调试现场设备容易得多。
l 使用足够(或更大)的堆栈大小。堆栈溢出是嵌入式系统内存损坏的头号原因。每个线程堆栈必须有足够的内存用于最坏情况下的函数调用嵌套,包括每个函数中的所有局部变量。否则,堆栈将溢出到堆栈前面的内存中。这种安全问题可以通过使用硬件堆栈限制功能来防止,或者使用MPU/MMU来保护堆栈正上方的区域。
l 显式指定并检查缓冲区大小。在所有提供缓冲区的函数中,调用方明确提供缓冲区的大小,被调用方明确检查大小以避免溢出,这一点很重要。
l 请注意内存损坏的可能性。当线程堆栈溢出时,它通常会破坏线程堆栈内存之前的内存(在大多数架构中,线程堆栈会向较低的地址增长)。相反,缓冲区溢出更有可能破坏紧跟在缓冲区后面的内存。因此,将关键数据放在堆栈之后、缓冲存储器之前通常更安全。
l 插入运行时间和位置堆栈和缓冲区之间的唯一数据模式,可用于在运行时检测内存损坏。
l 使用函数指针要非常小心。函数指针为不必要的程序执行提供了一条简单的路径——无论是无意的还是有意的。例如,将函数指针放在缓冲区内并不是好的做法,因为缓冲区溢出也可能会覆盖函数指针。在使用函数指针之前,通过一个小的散列或校验和来验证它们也是很好的。函数指针损坏是攻击者发起不想要的远程执行的最容易的方式,使你的嵌入式安全性变得不安全。
利用你的硬件安全资源并遵循嵌入式软件安全方面的一些最佳实践,将有助于提高你的嵌入式设备的安全性。还要记住,所有嵌入式安全措施——在设备端和设备所在的网络级别——都是整体深度防御策略的一部分。嵌入式安全的唯一目标是让对手更难获得不必要的访问!