理想的嵌入式软件一向兼具安全和防护设计。然而,“连网”给医疗、自动驾驶和物联网(IoT)设备等安全关键的应用中,带来了无法容忍程度的安全漏洞。
安全与防护的紧密结合,加上受到威胁程度的提高,使得开发者必须充分了解安全与防护之间的区别,而且从设计一开始就应用行业最佳实践,才能确保两者都被设计进产品中(图1)。
图1:过滤缺陷:理想的软件和硬件设计要求在整个设计过程中采用多层次质量保证、防御和安全保护。(来源:Barr Group)
随着物联网的崛起,系统现在很容易受到“远程攻击”的影响。最近的一起事件涉及索尼网络安全摄像机被发现存在后门帐户。这些端口可能被黑客用于使用僵尸网络(botnet)恶意软件感染系统,并发起更多攻击。索尼因此开发了固件补丁,用户可以下载来关闭后门。但实例中,还有许多编码或设计错误是不可恢复且可能造成灾难性后果的。
为了证明这点,两名安全研究人员曾经以远程无线方式 “黑”了一辆行驶中的Jeep Grand Cherokee,接管了仪表盘功能、方向盘、传动和刹车制动系统等。当然,这一“劫持”并非恶意,而是经过司机许可,让研究人员得以展示如何轻松地攻击网络运营商的互联网络有多么简单。尽管如此,这次的黑客入侵实验还是导致Chrysler召回了140万辆车。
当然,系统不一定非要连到互联网,才易受攻击、不安全:编写不佳的嵌入式代码和设计决策已造成这样的伤害了。例如1983年推出治疗癌症用的Therac-25放射治疗机,就是一个关于系统设计应该避免哪些错误的经典研究案例。软件错误、缺少硬件互锁,以及整体性较差的设计决策等多种因素结合在一起,导致了致命的辐射剂量。
导致Therac-25造成致命事故的元凶包括:
•不成熟和不充分的软件开发过程(“未经测试的软件”)
•不完整的可靠性建模和故障模式分析
•未针对关键软件进行(独立)审查
•旧版软件的重新使用不当
主要故障模式之一涉及频繁溢出的测试例程中的1字节计数器。如果操作人员在溢出时为机器提供手动输入,系统使用基于软件的互锁将会失效。
1996年6月,欧洲太空总署的火箭Ariane5(Flight 501)在发射后,偏离其预定的飞行计划,而不得不引爆自毁,这是由于为了求快,而省略了溢出检查所导致的。当一个保持水平速度的变量溢出时,就无法进行检测并作出适当响应。
尽管如此,关键的程序代码和安防漏洞仍然未得到审查。事实上,Barr Group的《2017年嵌入式系统安全与安防调查》显示,在工程师所进行的项目中,如果连接至互联网的项目被黑客攻击,就会整个挂掉:
•22%未将安全性能作为设计要求
•19%没遵循编码标准
•42%根本没有或只偶尔进行代码审查
•48%的人未对其在互联网上的通信进行加密
•超过33%未执行静态分析。
了解安全与防护的真正意义,是朝着弥补这一局面迈出的重要一步。
安全和防护(safety & security)这两个词经常被混用。有些开发者经常会有这样的误解:如果能编写出好的代码,那么项目就将是安全且受保护的。但显然不是。
一个“安全”的系统是指:在正常运行过程中,系统本身不会对用户,或其他任何人造成伤害的系统。“安全关键”(safety critical)系统是一种在故障时,可能导致伤害或伤亡的系统。因此,设计者的目标就是尽可能确保系统不出故障或者瘫痪。
另一方面,“防护”主要关注于产品在授权用户使用其资产的同时,也防范未经授权的接入(如黑客)的能力。这些资产包括流动或动态数据、代码和知识产权(IP)、处理器和系统控制中心、通信端口、内存和具有静态数据的存储器。
现在应该变得较明朗了,虽然系统能加以防护,但并不一定自动具有安全性:危险的系统也可能与安全可靠的系统一样具有防护性。然而,不具防护性的系统总是不安全的,因为即使一开始时它的功能是安全的,但其易于受到未经授权侵入的脆弱性,意味着它可能在任何时候变得不安全。
实现安全和防护设计
当谈到设计安全时,有很多因素要考虑,正如Therac-25的例子一样。然而,设计师只能控制其设计方面,而本文着重的是固件。
关键任务应用的一个很好例子是现代化汽车。这些车辆内可能有1亿多行代码,但却掌握在经常缺乏训练或分心的用户(驾驶员)手中。为了补强这部分用户的需求,以摄像机和传感器,以及车对基础设施(V2I)和车对车(V2V)通信的形式添加了更多的安全特征和代码。代码量不断增加,而且是呈指数级增长!
尽管海量代码使得这种系统的编码和调试更加困难,但如果遵循一些核心原则,则可以省去大部分调试时间,例如:
•对实时性能、成本、可升级性、安防性、可靠性和安全性有影响的硬件/软件分配
•实施容错区域。
•避免单点故障(图2)
•处理由代码错误、程序本身、内存管理或虚假中断引起的异常
•将溢出检查包括在内(Therac-25和Ariane火箭省略了)
•清理来自外界的污染数据(使用范围检查和CRC)。
•在每一层级进行测试(单元测试、集成测试、系统测试、模糊处理、校验和验证等)
图2:安全关键系统避免单点故障。(资料来源:美国卡内基梅隆大学教授Phil Koopman)
为安全起见,设计师或开发者需要熟悉用户和设备认证、公钥基础设施(PKI)和数据加密的复杂性。除了向授权用户提供资产和保护资产免受未经授权的访问外,安全性还意味着系统在面对攻击或故障时不会做不安全或者无法预料到的事。
当然,攻击有各种形式,包括基本拒绝服务(DoS)和分布式DoS(DDoS)。虽然开发者无法控制系统受到什么攻击,但他们可以控制系统对攻击的反应,且这种应对认知必须在全系统范围内实施。系统最薄弱的环节决定了系统的整体安全程度,而假设攻击者会发现该薄弱环节才是明智之举。
针对薄弱环节的示例之一就是远程固件更新(RFU),可通过设备的远程固件更新特性对系统进行攻击。此时的系统十分容易受到攻击,所以配备防范策略是明智之举,例如:让用户选择是禁用RFU,还是加载需对后续图像进行数字签名的更新。
这看起来似乎与直觉想法相反,但密码学基本不会是最弱环节。相反,攻击者会寻找由于实施、协议保护、API、用例和侧信道攻击等其它脆弱的攻击面。
在这些领域投入多少工作、时间和资源,取决于防护威胁的类型,每一种威胁都有具体的防范措施。开发者可以采取如下一些常见举措来提升产品的抗攻击能力:
•使用无外部存储器的微控制器
•禁用JTAG接口。
•实施安全启动。
•使用主密钥生成每个单元的设备专用密钥
•使用目标代码混淆
•实施开机自检(POST)和内建自测试(BIST)
说到“混淆”,有一种理论提倡“隐藏式防护”(security through obscurity)。但若只依赖该想法,却可能致命,因为每个秘密都会产生一个潜在的“软肋”。无论是通过社会工程(social engineering)、不满的员工,还是通过自卸和逆向工程等技术,秘密迟早都将不再是秘密。当然,隐藏式防护自有用处,例如让密钥保有秘密。
虽然有许多技术和技巧可以帮助开发者和设计师实现高度的安全性和防护性,但是有一些基本步骤可以确保系统在尽可能合理的情况下进行优化。首先,基于“久经考验”的编码规则、功能安全、行业和特定应用标准进行设计。这些准则包括MISRA和MISRA-C、ISO 26262、汽车开放系统架构(Autosar)、IEC 60335和IEC 60730等。
采用像MISRA这样的编码标准不仅有助于规避错误,还可以使代码更易阅读、一致及可移植(图3)。
图3:采用像MISRA这样的编码标准不仅有助于规避错误,还可以使代码更易阅读、一致及可移植(图3)。(来源:Barr Group)
其次,使用静态分析(图4)。这涉及分析软件,而非执行程序。它是种象征性执行,所以本质上是模拟。相比之下,在目标平台上运行实际的代码时,动态分析将会发现缺陷。
图4:静态分析工具运行源文件的“模拟”、语法和逻辑分析,并输出警告而非目标文件。(来源:Barr Group)
虽然静态分析并非灵丹妙药,但它确实增加了另一层保证,因为它能很好地检测潜在的错误;例如使用未初始化的变量、可能的整数溢出/下溢以及有符号和无符号数据类型的混用。此外,静态分析工具正在不断改善中。
通常,静态分析意味着使用专用工具(如PC-Lint或Coverity),但开发者还应考虑重新分析自己的代码。
第三,进行代码审查。这将提高代码的正确性,同时也有助于可维护性和可扩展性。代码审查还有助于召回/保修维修和产品责任索赔。
第四,进行威胁建模。从使用攻击树开始。这要求开发者像攻击者一样思考并执行以下操作:
•确定攻击目标:
o每次攻击都有一棵单独的树
•对于每棵树(目标):
o确定不同的攻击
o确定每次攻击的步骤和选项
值得注意的是,若从多个角度进行此类分析,则可大幅提高其效益。
显而易见,执行上述四个基本步骤就能轻松地减少错误,并增加安全性和防护性;但这需要时间,因此,开发者必须进行相应的时间预算。虽然项目规模不同,但重要的是必须尽可能实际。
例如,添加15%到50%的设计时间,以利于代码审查。一些系统需要完整的代码审查;有些不需要。静态分析工具可能需要10到数百小时进行初始设置,但一旦进入开发过程的某一部分或阶段,产品开发就无需额外时间进行产品开发了,他们最终都通过更好的系统获得回报。
连网技术让嵌入式系统设计增加了新的顾虑,它需要特别强调安全性和防护性。对这两个概念的详细了解、加上在设计周期之初就适当地应用最佳实践,能大大提高产品的整体安全性和防护性。这些最佳实践包括:采用编码标准、使用静态分析工具、代码审查和威胁建模。
本文来自《电子工程专辑》2017年12月刊,版权所有,谢绝转载