首先,非常快速的历史课:内存安全语言已经存在了几十年。例如,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 并调用代码正确。