创建路径查询¶
您可以创建路径查询来可视化信息在代码库中的流动。
注意
此处描述的新型模块化数据流 API 与 CodeQL 2.13.0 及更高版本中的先前库并存。有关库更改方式以及如何将任何现有查询迁移到模块化 API 的信息,请参阅 用于 CodeQL 查询编写的全新数据流 API。
概述¶
安全研究人员特别关注信息在程序中的流动方式。许多漏洞是由看似良性的数据流向意外位置并以恶意方式使用引起的。使用 CodeQL 编写的路径查询对于分析数据流特别有用,因为它们可以用来跟踪变量从其可能的起始点 (source
) 到其可能的终点 (sink
) 所经过的路径。要对路径进行建模,您的查询必须提供有关 source
和 sink
的信息,以及将它们链接起来的数据流步骤。
本主题提供有关如何构建路径查询文件的信息,以便您可以探索与数据流分析结果相关的路径。
注意
路径查询生成的警报包含在使用 CodeQL CLI 生成的结果中,以及在 代码扫描 中。您还可以在 用于 VS Code 的 CodeQL 扩展 中查看路径查询生成的路径说明。
要了解有关使用 CodeQL 对数据流进行建模的更多信息,请参阅“关于数据流分析。”有关分析数据流的更多特定于语言的信息,请参阅
- “在 C/C++ 中分析数据流”
- “在 C# 中分析数据流”
- “在 Java/Kotlin 中分析数据流”
- “在 JavaScript/TypeScript 中分析数据流”
- “在 Python 中分析数据流”
- “在 Ruby 中分析数据流”
- “在 Swift 中分析数据流”
路径查询示例¶
开始编写自己的路径查询的最简单方法是修改现有查询之一。有关更多信息,请参阅 CodeQL 查询帮助。
安全实验室研究人员已使用路径查询来查找各种开源项目中的安全漏洞。要查看描述这些查询是如何编写的文章,以及其他描述安全研究其他方面的文章(例如利用漏洞),请参阅 GitHub 安全实验室网站。
构建路径查询¶
路径查询需要某些元数据、查询谓词和 select
语句结构。CodeQL 中包含的许多内置路径查询都遵循简单的结构,这取决于您正在分析的语言是如何使用 CodeQL 建模的。
您应使用以下模板
/**
* ...
* @kind path-problem
* ...
*/
import <language>
// For some languages (Java/C++/Python/Swift) you need to explicitly import the data flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...
module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"
其中
MyConfiguration
是一个模块,其中包含定义数据如何在source
和sink
之间流动的谓词。Flow
是基于MyConfiguration
的数据流计算的结果。Flow::Pathgraph
是您需要导入以在查询中包含路径说明的结果数据流图模块。source
和sink
是图中的节点,如配置中所定义,Flow::PathNode
是它们的类型。DataFlow::Global<..>
是数据流的调用。可以使用TaintTracking::Global<..>
来代替,以包含一组默认的附加污染步骤。
以下部分描述了有效路径查询的主要要求。
路径查询元数据¶
路径查询元数据必须包含属性 @kind path-problem
- 这将确保查询结果被正确解释和显示。其他元数据要求取决于您打算如何运行查询。有关更多信息,请参阅“CodeQL 查询的元数据。”
生成路径说明¶
为了生成路径说明,您的查询需要计算一个图。为此,您需要在查询中定义一个名为 edges
的 查询谓词。此谓词定义了您正在计算的图的边关系,它用于计算与查询生成的每个结果相关的路径。您可以从数据流库中的一个路径图模块导入预定义的 edges
谓词。除了路径图模块之外,数据流库还包含在数据流分析中常用的其他 classes
、predicates
和 modules
。
import MyFlow::PathGraph
此语句从数据流库 (DataFlow.qll
) 中导入 PathGraph
模块,其中定义了 edges
。
您还可以导入专门设计用于在各种常见框架和环境中实现数据流分析的库,并且 CodeQL 附带了许多其他库。要查看数据流分析中使用的不同库的示例,请参阅上述内置查询的链接,或浏览 标准库。
对于所有语言,您还可以选择定义 nodes
查询谓词,它指定您感兴趣的路径图的节点。如果定义了 nodes
,则仅选择端点由这些节点定义的边。如果未定义 nodes
,则您选择所有可能的 edges
端点。
定义您自己的 edges
谓词¶
您也可以在查询主体中定义您自己的 edges
谓词。它应该采用以下形式
query predicate edges(PathNode a, PathNode b) {
/* Logical conditions which hold if `(a,b)` is an edge in the data flow graph */
}
有关如何定义 edges
谓词的更多示例,请访问 标准 CodeQL 库 并搜索 edges
。
声明源和接收器¶
您必须在路径查询中提供有关 source
和 sink
的信息。这些对象对应于您正在探索的路径的节点。在查询的 from
语句中必须声明 source
和 sink
的名称和类型,并且类型必须与由 edges
谓词计算的图的节点兼容。
如果您正在查询 C/C++、C#、Go、Java/Kotlin、JavaScript/TypeScript、Python 或 Ruby 代码(并且您在查询中使用了 import MyFlow::PathGraph
),则 source
和 sink
的定义可通过数据流库中 Global<..>
模块应用后产生的模块访问。您应在 from
语句中声明这两个对象。例如
module MyFlow = DataFlow::Global<MyConfiguration>;
from MyFlow::PathNode source, MyFlow::PathNode sink
必须定义配置模块以包含源和接收器的定义。例如
module MyConfiguration implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { ... }
predicate isSink(DataFlow::Node source) { ... }
}
isSource()
定义了数据可能从中流出的位置。isSink()
定义了数据可能流入的位置。
有关在分析中使用配置类的更多信息,请参阅“在 C/C++ 中分析数据流”、“在 C# 中分析数据流”和“在 Python 中分析数据流”中的全局数据流部分。
您还可以通过扩展 Configuration
类来为不同的框架和环境创建配置。有关更多信息,请参阅 QL 语言参考中的“类型”。
定义流条件¶
在 from
语句中声明的变量上,where
语句定义了用于生成结果的逻辑条件。该语句可以使用 聚合、谓词 和逻辑 公式 将感兴趣的变量限制在满足定义条件的较小子集中。
在编写路径查询时,通常会包含一个仅当数据从 source
流向 sink
时才成立的谓词。
可以使用 flowPath
谓词来指定给定 Configuration
的数据从 source
流向 sink
。
where MyFlow::flowPath(source, sink)
选择语句¶
路径查询的选择语句包含四个“列”,结构如下
select element, source, sink, string
如“关于 CodeQL 查询”中所述,element
和 string
列分别表示警报的位置和警报消息。第二列和第三列 source
和 sink
是查询选择的路径图上的节点。查询生成的每个结果都以与警报查询相同的方式显示在同一位置。此外,每个结果还与一个关联路径,可以在 VS Code 的 CodeQL 扩展 中查看。
在第一列中选择 element
取决于查询的目的和它旨在查找的问题类型。这对于安全问题尤其重要。例如,如果您认为 source
值在全局上无效或恶意,则最好将警报显示在 source
上。相反,如果您认为 sink
是需要进行清理的元素,则应考虑将警报显示在 sink
上。
select
语句中最后一列定义的警报消息可以通过使用链接和占位符来进一步开发,以提供有关查询发现的警报或路径的更多详细信息。有关更多信息,请参阅“定义查询的结果”。
进一步阅读¶
- GitHub 文档中的 使用路径查询探索数据流。
- CodeQL 仓库