https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-java/
一、本地数据流
本地数据流的作用域限定在一个方法内、一个调用内。
本地数据流相比全局数据流更easier,更faster,更precise。
本地数据流相关的库位于DataFlow模块中,需要手动导入。
import semmle.code.java.dataflow.DataFlow
数据流节点(Node)可以分为ExprNode和ParamterNode。
DataFlow::Node的两个谓词,可以将数据流节点转Expr、Parameter的形式
class Node {// 获取数据流节点Expr的形式Expr asExpr() { ... }// 获取数据流节点Parameter的形式Parameter asParameter() { ... }}
DataFlow的两个谓词,可以将Expr、Parameter转数据流节点的形式
// 获取Expr对应的数据流节点形式ExprNode DataFlow::exprNode(Expr e)
// 获取Parameter对应的数据流节点形式ParameterNode DataFlow::parameter(Parameter p)
1.1 使用本地数据流
可以通过DataFlow的谓词localFlowStep限定从nodeFrom流向nodeTo的数据流
DataFlow::localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)
localFlowStep可以使用递归操作符+和,DataFlow的谓词localFlow相当于localFlowStep
代码示例
import javaimport semmle.code.java.dataflow.DataFlowfrom Parameter source, Expr sinkwhere DataFlow::localFlow(DataFlow::parameterNode(source), DataFlow::exprNode(sink))select source,sink
匹配结果示例
1.2 使用本地污点追踪
关于污点追踪:如下示例代码中,如果x被定义为污点,那么y也将是污点。
String temp = x;String y = temp + ", " + temp;
本地污点追踪库位于semmle.code.java.dataflow.TaintTracking之中,需要手动导入。
类似本地数据流分析,可以使用TaintTracking的谓词localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)限定从nodeFrom流向nodeTo的数据流。
localTaintStep可以使用递归操作符+和,localTaint等价于localTaintStep
TaintTracking::localTaint(DataFlow::parameterNode(source), DataFlow::exprNode(sink))
代码示例
import javaimport semmle.code.java.dataflow.DataFlowimport semmle.code.java.dataflow.TaintTrackingfrom Parameter source, Expr sinkwhere TaintTracking::localTaint(DataFlow::parameterNode(source), DataFlow::exprNode(sink))select source,sink
匹配结果示例

1.3 案例
限定sink是java.io.FileReader的第一个参数,利用本地数据流分析寻找所有的source
import javaimport semmle.code.java.dataflow.DataFlowfrom Constructor fileReader, Call call, Parameter pwherefileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") andcall.getCallee() = fileReader andDataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(0)))select p
匹配结果示例
二、全局数据流
全局数据流比本地数据流更强大,但是执行时也更消耗时间与内存。
2.1 使用全局数据流
需要继承DataFlow::Configuration
import semmle.code.java.dataflow.DataFlowclass MyDataFlowConfiguration extends DataFlow::Configuration {MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" }override predicate isSource(DataFlow::Node source) {...}override predicate isSink(DataFlow::Node sink) {...}}
Configuration内置的几个谓词
isSource—定义数据流的来源isSink—定义数据流的终点isBarrier—可选,限制数据流isAdditionalFlowStep—可选,限制数据流的步数
然后通过调用Configuration的谓词hashFlow(DataFlow::Node source, DataFlow::Node sink)来执行数据流分析。
from MyDataFlowConfiguration dataflow, DataFlow::Node source, DataFlow::Node sinkwhere dataflow.hasFlow(source, sink)select source, "Data flow to $@.", sink, sink.toString()
2.2 使用全局污点追踪
需要继承TaintTracking::Configuration
import semmle.code.java.dataflow.TaintTrackingclass MyTaintTrackingConfiguration extends TaintTracking::Configuration {MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }override predicate isSource(DataFlow::Node source) {...}override predicate isSink(DataFlow::Node sink) {...}}
These predicates are defined in the configuration:
isSource—defines where taint may flow fromisSink—defines where taint may flow toisSanitizer—optional, restricts the taint flowisAdditionalTaintStep—optional, adds additional taint steps
全局污点追踪的执行与全局数据流的执行相似,也是通过调用Configuration的hasFlow谓词。
三、内置的数据流来源
Java库中内置了一些数据流源。
例如RemoteFlowSource类(需要导入包semmle.code.java.dataflow.FlowSources)限制了数据流源来自远端的用户。
代码示例1
import javaimport semmle.code.java.dataflow.FlowSourcesfrom RemoteFlowSource sourceselect source
匹配结果示例1
request.getHeader(“x-requested-with”);
request.getRequestURL();
req.getParameter(paramName);
代码示例2
远端数据流来源 + 全局污点追踪
import javaimport semmle.code.java.dataflow.DataFlowimport semmle.code.java.dataflow.FlowSourcesimport semmle.code.java.dataflow.TaintTrackingclass MyTaintTrackingConfiguration extends TaintTracking::Configuration {MyTaintTrackingConfiguration() {this = "MyTaintTrackingConfiguration"}override predicate isSource(DataFlow::Node source) {source instanceof RemoteFlowSource}override predicate isSink(DataFlow::Node sink) {exists(Call call |sink.asExpr() = call.getArgument(0))}}from DataFlow::Node src, DataFlow::Node sink, TaintTracking::Configuration configwhere config.hasFlow(src, sink)select src, sink
匹配结果示例2

