一种新的编程范式(例如,Rust)来减少或终止所有零日漏洞/利用?

信息安全 零日 渗出
2021-08-14 06:55:36

鉴于 Hafnium 和 Solarwind 黑客攻击,其中多个零日漏洞被用于最终进行黑客攻击和数据泄露,使用诸如 Rust 之类的内存安全编程语言来构建软件是否有助于减少或结束所有这些零日漏洞通过编程范例的漏洞/利用?

更重要的是,我们可以做些什么(架构、编程范式)来减少/阻止零日漏洞/利用,从而使数据泄露成为过去?

那里有如此多的安全工具,如此多的复杂网络安全提供商,但似乎没有一个能够阻止这些零日漏洞/利用——也许解决方案更容易并且在我们掌握之中?不是使用拼凑而成的工具,而是像用于开发软件的语言这样基本的东西是结束所有零日漏洞/利用的关键吗?

4个回答

只要人类在制作软件,0-days 就会成为现实。

单个exec/eval通常足以用于漏洞利用。使用像 Rust 或高级语言这样的内存安全语言大多可以消除内存问题,但会引入整个类别的攻击向量,例如 Python 等解释语言的代码注入。还有逻辑错误,例如TOCTOU错误,off-by-1错误,忘记检查某些参数。

将恶意代码放入您的软件中,因为您从互联网上的陌生人那里复制粘贴了一些代码,而没有检查代码或接受恶意或新手程序员提出的拉取请求,这是您在不知情的情况下将错误引入代码的另一种方式。

人类容易犯错误,并且会继续犯愚蠢的错误,无论你喜欢与否。0-days不仅仅是内存问题,它们通常是软件无法捕捉到的逻辑错误,需要人来检查。

TLDR:Rust可能是一种改进,但它不是灵丹妙药。

Rust 编程语言旨在减少未定义行为导致的漏洞。

Rust未定义的行为

首先:使用 rust 并不会将未定义行为导致的漏洞几率降低到零,因为 rust 具有unsafe并且滥用unsafe导致 UB。unsafe出于务实的原因而存在。大多数应用程序将依赖于少量的不安全代码(可能作为依赖项包含而不是由应用程序的开发人员编写)。rust 的承诺是,与 C 和 C++ 相比,它可以使查找和修复 UB 更具可扩展性。只有不安全代码中的错误才会导致 UB。犯错的机会更少 -> 错误更少。

UB只是杯水车薪

一个更大的问题是 UB 只是众多漏洞来源之一。以下是 Rust 无法保护您免受漏洞影响的一些示例:

配置也可能容易受到攻击

建立一个即使是傻瓜也能使用的系统,只有傻瓜才会想要使用它。- 肖氏原理

正如亚当巴恩斯在他的回答中指出的那样,即使不利用软件错误,数据泄露也是可能的。大多数软件都有一些配置机制,以便能够适应许多不同的环境和用例,或者能够适应不断变化的环境。由于 Shaw 原则,这种配置机制允许不安全的配置并不少见。此外:身份验证通常依赖于对一条信息(密码、令牌、加密密钥等)进行保密,如果该信息不是保密的,则会中断。

以下是一些不安全配置的示例:

首先,非常快速的历史课:内存安全语言已经存在了几十年。例如,Java 具有极强的内存安全性(除了可能导致崩溃但不会导致内存损坏的空引用异常的风险);它不公开指针(地址)并且不允许手动内存管理。然而,用 Java 编写的大量代码仍然始终存在漏洞。

为什么?

嗯,其中一些是,最终,使其内存安全的语言特性必须在实际的机器逻辑中实现,这显然不是内存安全的。java.util.ArrayList如果使用本机内存缓冲区实现所有访问都保证边界检查的语言没有任何好处ArrayList,由于某些指针算法导致某些平台上的整数溢出,有时认为索引是安全的不是。任何在实际的、真实的、广泛使用的指令集上实现的语言都将面临这个问题,因为 CPU 所理解的只是地址和值。

但即使除了编译器或运行时中的错误之外,也不乏逻辑错误。Shell 注入,如 SQL 注入和 XSS(实际上应该称为“脚本注入”或“html 注入”),会导致任意代码执行而不会造成任何内存损坏。缺少授权检查,其中一些应该只允许某些用户访问的对象允许每个人代替,或者对于应该是 RO 的组的某些内容是 R/W,这是常见的,并且可以访问各种事物。加密错误,例如重复使用相同的 IV/nonce 和密钥进行多个操作,或者未能包括对加密数据的完整性检查,可能会破坏任何依赖它们的系统。欺骗攻击,

设计一种语言是不可能的,即使在理论上,它仍然是图灵完备的。


有人尝试使用特定的代码块来执行此操作。“可证明的正确性”,您尝试详尽地指定输入空间并将其全部映射到正确的输出,然后验证(通常通过静态分析)输入是否都产生正确的输出,这是一种尝试。然而,即使程序不是太复杂以至于这种证明是可行的,这个想法也会失败,因为“输入”包括程序运行的整个环境,而“输出”包括代码的所有可检测效果(不是只是它返回的实际值)。将 128 字节字符串作为输入并返回 true 的算法,如果它与某个秘密值完全匹配,而不会暴露秘密本身,这很容易证明是正确的。然而,如果它使用提前退出算法(其中不匹配的第一个字节会导致函数立即返回 false),那么定时攻击会降低从“暴力破解直到宇宙热死”来找到秘密的难度没有接近”到“这可能需要几个小时,具体取决于时间信息的嘈杂程度”。这就是可证明正确性的问题:如果您不考虑诸如“函数花费的时间必须是恒定的”之类的事情,您就不会将“函数花费的时间”视为输出,因此不会注意到具有较长匹配子串的错误输入需要更长的时间才能产生输出;您只会看到它们仍然返回 false 并调用代码正确。t match 导致函数立即返回 false)然后定时攻击将发现秘密的难度从“暴力破解直到宇宙热死而没有接近”降低到“这可能需要几个小时,具体取决于如何嘈杂的时间信息是“。这就是可证明正确性的问题:如果您不考虑诸如“函数花费的时间必须是恒定的”之类的事情,您就不会将“函数花费的时间”视为输出,因此不会注意到具有较长匹配子串的错误输入需要更长的时间才能产生输出;您只会看到它们仍然返回 false 并调用代码正确。t match 导致函数立即返回 false)然后定时攻击将发现秘密的难度从“暴力破解直到宇宙热死而没有接近”降低到“这可能需要几个小时,具体取决于如何嘈杂的时间信息是“。这就是可证明正确性的问题:如果您不考虑诸如“函数花费的时间必须是恒定的”之类的事情,您就不会将“函数花费的时间”视为输出,因此不会注意到具有较长匹配子串的错误输入需要更长的时间才能产生输出;您只会看到它们仍然返回 false 并调用代码正确。

如果你真的很聪明,这个问题就会变成一个无法解决的分类问题,因为类别定义不明确。SMTP 客户端是数据泄露零日漏洞吗?如果它能够发送包含私钥文件的消息怎么办?如果该消息的收件人是授权人怎么办?如果接收服务器被入侵怎么办?

无论未来是人类还是机器编写程序,使用它们的环境将始终是您是否希望它们运行的​​最终决定因素。