检测漏洞
对于无法完全防止引入一类漏洞的现有源代码,例如,由于编程语言和/或API的选择是由其他因素决定的,应用技术来检测是有用的在软件的开发、测试和/或维护阶段,代码中存在漏洞。
(相关资料图)
检测漏洞的技术必须在检测技术可以具有的以下两个良好属性之间进行权衡:
•如果检测技术可以正确得出结论,给定程序没有该类别的漏洞,则检测技术对于给定类别的漏洞是合理的。另一方面,不健全的检测技术可能有假阴性,即检测技术无法发现的实际漏洞。
•对于给定类别的漏洞,如果检测到的任何漏洞是实际漏洞,则检测技术是完整的。另一方面,不完整的检测技术可能存在误报,即它可能会检测到并非实际漏洞的问题。
权衡是必要的,因为根据赖斯定理(对于非平凡的漏洞类别)没有一种检测技术既健全又完整。
实现健全性需要对程序的所有执行进行推理(通常是无限多个)。这通常是通过对程序代码进行静态检查来完成的,同时对执行进行适当的抽象以使分析终止。
通过执行程序的实际、具体执行来实现完整性,该程序见证了报告的任何漏洞。这通常是通过动态检测完成的,其中分析技术必须为触发漏洞的程序提供具体的输入。一种非常常见的动态方法是软件测试,其中测试人员编写具有具体输入的测试用例,并对相应的输出进行特定检查。
在实践中,检测工具可以使用静态和动态分析技术的混合组合,以实现健全性和完整性之间的良好权衡。
然而,必须指出的是,一些检测技术本质上是启发式的,因此没有为它们精确定义健全性和完整性的概念。例如,检测违反安全编码实践的启发式技术(如2.3中所述)正在检查是否符合非正式定义的规则和建议,并且并不总是能够明确定义误报。或假阴性。此外,这些方法可能会突出“漏洞”,这些漏洞目前可能无法利用,但仍然应该修复,因为它们是“差点错过”,即将来的维护错误可能很容易被利用。
静态和动态程序分析技术在计算机科学的其他领域被广泛研究。本主题重点介绍与软件安全最相关的分析技术。
检测漏洞的另一种重要方法是执行手动代码审查和审核。这些技术在安全软件生命周期CyBOKKnowl边缘领域[1]中有所介绍。使用工具支持的静态检测时,调整此类后续代码审查和其他验证活动是有意义的。例如,如果静态检测对于给定类别的漏洞是合理的,那么可以考虑在以后的阶段不审查或测试该类别的漏洞。
静态检测
静态检测技术分析程序代码(源代码或二进制代码)以查找漏洞。与动态技术相反,静态技术的优点是它们可以对(尚)不可执行的不完整代码进行操作,并且可以在单个分析中进行操作。运行它们尝试涵盖所有可能的程序执行。粗略地说,人们可以区分两类重要的技术,它们的主要目标不同。
启发式静态检测
首先,有一些静态分析技术可以检测违反规则的情况,这些规则是安全编程实践启发式的正式编码。静态分析技术构建了程序的语义模型,例如,包括抽象语法树,以及程序中数据流和控制流的抽象。基于此模型,该技术可以标记违反简单语法规则的行为,例如,不要使用此危险的API函数,或者仅将此API函数与常量字符串一起使用。参数。
漏洞存在的一个重要指标是(可能是恶意的)程序输入可能会影响风险操作中使用的值(例如,索引到数组中,或连接字符串以创建SQL查询)。污点分析(有时也称为流分析)是一种分析技术,用于确定值是否来自程序输入(或更一般地来自指定的污点源))可以影响此类风险操作中使用的值(或者更一般地说,影响流入受限接收器的值)。相同的分析还可用于检测程序中的机密或敏感信息流向公共输出通道的情况。
存在许多静态污点分析的变体。重要的变化包括(1)代码的牵引力,例如,路径敏感与路径不敏感,或上下文敏感与上下文不敏感分析,以及(2)是否由程序控制流而不是程序引起的影响考虑数据流(通常通过使用术语污点分析与信息流分析来区分)。
为了减少误报的数量,污点分析可以考虑程序执行的清理。由指定的清理功能处理的污染值(假设用于验证这些值对进一步处理无害)将删除其污点。
一个重要的挑战是,必须为污点分析配置正确的源、水槽和消毒剂。在实践中,这种配置目前经常手动进行,尽管最近的一些工作增加了工具辅助,例如,机器学习用于支持安全分析师完成这项任务。
声音静态验证
其次,有一些静态分析技术旨在针对明确定义的漏洞类别保持合理性(但通常在实践中仍然会做出妥协并放弃对漏洞的合理性某种程度上)。对于可以理解为违反规范或合同的漏洞类别,主要挑战是正式表达此底层规范。完成此操作后,在计算机科学其他领域开发的大量静态分析和程序验证知识可用于检查是否符合规范。三种主要的相关技术是程序验证、抽象解释和模型检查。
程序验证使用程序逻辑来表达程序规范,并依赖于程序员/验证器以以下形式提供程序的充分抽象:归纳循环不变量或函数前置和后置条件,以便能够构造涵盖所有程序执行的证明。对于具有动态内存分配的命令式语言,分离逻辑[26]是一种程序逻辑,可以表示不存在内存管理和竞争条件漏洞(对于存储单元上的数据争用),以及符合程序员提供了程序API的合同。检查是否符合分离逻辑规范通常不是自动的:它通过交互式程序验证完成,其中程序注释用于提供不变量、前置条件和后置条件。但是,如果一个人只对没有内存管理漏洞感兴趣,有时可以推断出这些注释,使该技术自动进行。此外,避免使用某些语言功能(例如指针),并坚持适合验证的编码风格,有助于使验证自动化。
抽象解释是一种自动技术,通过将程序操作的运行时值映射到足够的有限抽象域,从具体程序进行抽象。对于不使用动态分配或递归的命令式程序,抽象解释是一种成功的技术,可以自动有效地证明不存在内存管理漏洞。
模型检查是一种自动技术,它详尽地探索程序的所有可访问状态,以检查是否没有状态违反给定规范。由于状态爆炸问题,模型检查只能穷尽地探索非常小的程序,在实践中需要使用绑定探索的技术,例如,通过限制程序循环的次数执行。有界模型检查不再合理,但仍能发现许多漏洞。
这些分析技术的大多数实际实现在某种程度上放弃了合理性。为了既合理又终止,静态分析必须过度近似它所分析的程序的可能行为。过度逼近会导致误报。真正的编程语言具有很难在不导致不可接受的误报数量的情况下过度近似的功能。因此,实际实现必须进行工程权衡,并且会低估某些语言功能。这使得实现不合理,但在减少误报数的意义上更有用。这些工程权衡在“声音宣言”中得到了很好的总结[27]。
动态检测
动态检测技术执行程序并监视执行以检测漏洞。因此,如果足够高效,它们也可用于实时漏洞缓解(请参阅主题4)。动态检测有两个重要且相对独立的方面:(1)应该如何监视执行以检测到漏洞,以及(2)执行多少程序以及执行哪些程序(3)即应该监控哪些输入值?
监测
对于可理解为违反单个执行的指定属性的漏洞类别(请参阅主题1.6),可以通过监视违反该规范来执行完整检测。对于其他类别的漏洞,或者当监视违反规范的成本太高时,可以定义近似监视器。
对内存管理漏洞的监视已经进行了深入研究。原则上,可以构建完整的监视器,但通常需要花费大量的时间和内存。因此,现有工具探索了执行速度、内存使用和完整性方面的各种权衡。现代C编译器包括用于生成代码以监视内存管理漏洞的选项。在动态分析是近似分析的情况下(如静态分析),它也可能生成误报或漏报,尽管它对具体的执行跟踪进行操作。
对于结构化输出生成漏洞,一个挑战是生成的输出的预期结构通常是隐式的,因此没有可以监视的显式规范。因此,监视依赖于合理的启发式方法。例如,监视器可以使用细粒度的动态污点分析[25]来跟踪不受信任的输入字符串的流,然后在不受信任的输入对生成的解析树产生影响时标记违规。输出。
软件构建的合同设计方法[18,c3]支持的断言,前置条件和后置条件可以编译到代码中,以便在测试时提供API漏洞的监视器,即使这些编译的运行时检查的成本可能太高而无法在生产代码中使用它们。
监控竞争条件很困难,但存在一些用于监控共享内存单元上数据争用的方法,例如,通过监视所有共享内存访问是否遵循一致的锁定规则。
生成相关执行
动态检测技术的一个重要挑战是沿着导致发现新漏洞的路径生成程序的执行。这个问题是软件测试中系统地为被测程序选择适当输入的一般问题的一个例子[18,c4]。这些技术通常由模糊测试或模糊测试的总称描述,可分为:
•黑盒模糊测试,其中输入值的生成仅取决于被测试程序的输入/输出行为,而不取决于其内部结构。已经提出了许多不同的黑盒模糊测试变体,包括(1)纯随机测试,其中输入值从适当的值域中随机采样,(2)模型基于模糊测试,其中在生成输入值期间考虑输入值的预期格式(通常以语法形式)的模型,以及(3)基于突变的模糊测试,其中模糊器提供一个或多个典型输入值,并通过对提供的输入执行小突变来生成新的输入值值。
•白盒模糊测试,分析程序的内部结构以帮助生成适当的输入值。主要的系统白盒模糊测试技术是动态符号执行。动态符号执行执行具有具体输入值的程序,并同时构建路径条件,这是一个逻辑表达式,用于指定程序采用此特定执行路径必须满足的那些输入值的约束。通过求解不满足当前执行路径条件的输入值,模糊器可以确保这些输入值将程序驱动到不同的执行路径,从而提高覆盖率。
标签: