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 java
import semmle.code.java.dataflow.DataFlow
from Parameter source, Expr sink
where 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 java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
from Parameter source, Expr sink
where TaintTracking::localTaint(DataFlow::parameterNode(source), DataFlow::exprNode(sink))
select source,sink
匹配结果示例
1.3 案例
限定sink是java.io.FileReader的第一个参数,利用本地数据流分析寻找所有的source
import java
import semmle.code.java.dataflow.DataFlow
from Constructor fileReader, Call call, Parameter p
where
fileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") and
call.getCallee() = fileReader and
DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(0)))
select p
匹配结果示例
二、全局数据流
全局数据流比本地数据流更强大,但是执行时也更消耗时间与内存。
2.1 使用全局数据流
需要继承DataFlow::Configuration
import semmle.code.java.dataflow.DataFlow
class 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 sink
where dataflow.hasFlow(source, sink)
select source, "Data flow to $@.", sink, sink.toString()
2.2 使用全局污点追踪
需要继承TaintTracking::Configuration
import semmle.code.java.dataflow.TaintTracking
class 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 java
import semmle.code.java.dataflow.FlowSources
from RemoteFlowSource source
select source
匹配结果示例1
request.getHeader(“x-requested-with”);
request.getRequestURL();
req.getParameter(paramName);
代码示例2
远端数据流来源 + 全局污点追踪
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
class 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 config
where config.hasFlow(src, sink)
select src, sink