关于数据流分析¶
数据流分析用于计算变量在程序中各个点可能具有的值,确定这些值如何在程序中传播以及在何处使用。
概述¶
许多 CodeQL 安全查询实现数据流分析,这可以突出显示可能导致代码库中漏洞的潜在恶意或不安全数据的命运。这些查询可以帮助您了解数据是否以不安全的方式使用,是否将危险参数传递给函数,或者敏感数据是否会泄漏。除了突出显示潜在的安全问题外,您还可以使用数据流分析来了解程序行为的其他方面,例如查找未初始化变量的使用和资源泄漏。
以下部分简要介绍了 CodeQL 的数据流分析。
有关分析特定语言中的数据流的更多信息,请参阅以下教程
- “分析 C/C++ 中的数据流”
- “分析 C# 中的数据流”
- “分析 Java/Kotlin 中的数据流”
- “分析 JavaScript/TypeScript 中的数据流”
- “分析 Python 中的数据流”
- “分析 Ruby 中的数据流”
注意
数据流分析在路径查询中得到广泛使用。要详细了解路径查询,请参阅“创建路径查询。”
数据流图¶
CodeQL 数据流库通过对程序或函数的数据流图进行建模,在程序或函数上实现数据流分析。与 抽象语法树 不同,数据流图不反映程序的语法结构,而是对数据在运行时通过程序的流动方式进行建模。抽象语法树中的节点表示语法元素,例如语句或表达式。另一方面,数据流图中的节点表示在运行时携带值的语义元素。
一些 AST 节点(如表达式)具有相应的数据流节点,而另一些节点(如 if
语句)则没有。这是因为表达式在运行时计算为一个值,而 if
语句纯粹是一种控制流结构,不携带值。还有一些数据流节点根本不对应于 AST 节点。
数据流图中的边表示数据在程序元素之间流动的方向。例如,在表达式 x || y
中,存在与子表达式 x
和 y
对应的节点,以及与整个表达式 x || y
对应的节点。从与 x
对应的节点到与 x || y
对应的节点有一条边,表示数据可能从 x
流向 x || y
(因为表达式 x || y
可能计算为 x
)。同样,从与 y
对应的节点到与 x || y
对应的节点也有一条边。
局部数据流和全局数据流的区别在于它们考虑的边:局部数据流只考虑属于同一函数的数据流节点之间的边,并忽略函数之间以及通过对象属性的数据流。但是,全局数据流也会考虑后者。污点跟踪在数据流图中引入了额外的边,这些边并不完全对应于值的流动,而是对运行时某个值是否可能从另一个值派生进行建模,例如通过字符串操作。
数据流图是使用 类 计算的,用于对表示图节点的程序元素进行建模。数据在节点之间的流动是使用 谓词 建模的,用于计算图的边。
计算准确且完整的数据流图存在一些挑战
- 无法计算通过标准库函数的数据流,因为源代码不可用。
- 某些行为直到运行时才会确定,这意味着数据流库必须采取额外步骤来查找潜在的调用目标。
- 变量之间的别名可能导致单个写入更改多个指针指向的值。
- 数据流图可能非常大,计算起来很慢。
为了克服这些潜在的问题,库中对两种类型的数据流进行了建模
- 局部数据流,涉及单个函数内的数据流。在推理局部数据流时,您只考虑属于同一函数的数据流节点之间的边。它通常足够快、高效且精确,足以满足许多查询,并且通常可以为 CodeQL 数据库中的所有函数计算局部数据流。
- 全局数据流,通过计算函数之间以及通过对象属性的数据流,实际上考虑了整个程序内的數據流。计算全局数据流通常比局部数据流需要更多时间和精力,因此查询应进行细化以查找更具体的源和接收器。
许多 CodeQL 查询都包含局部数据流分析和全局数据流分析的示例。有关更多信息,请参阅 CodeQL 查询帮助。
正常数据流与污点跟踪¶
在标准库中,我们区分了“正常”数据流和污点跟踪。正常数据流库用于分析信息流,其中数据值在每个步骤中都得以保留。
例如,如果您正在跟踪不安全的对象 x
(这可能是某些不受信任或潜在的恶意数据),程序中的某个步骤可能会“更改”其值。因此,在一个简单的过程中,例如 y = x + 1
,正常数据流分析将突出显示 x
的使用,但不会突出显示 y
的使用。但是,由于 y
是从 x
派生的,因此它受到不受信任或“污染”信息的影响,因此它也被污染。分析污点从 x
到 y
的流动称为污点跟踪。
在 QL 中,污点跟踪通过包括数据值不一定保留的步骤扩展了数据流分析,但潜在的不安全对象仍然会传播。这些流步骤在污点跟踪库中使用谓词进行建模,这些谓词如果污点在节点之间传播则成立。
进一步阅读¶
- 使用路径查询探索数据流 在 GitHub 文档中。