https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-java/

一、本地数据流

本地数据流的作用域限定在一个方法内、一个调用内。
本地数据流相比全局数据流更easier,更faster,更precise。

本地数据流相关的库位于DataFlow模块中,需要手动导入。

  1. import semmle.code.java.dataflow.DataFlow

数据流节点(Node)可以分为ExprNode和ParamterNode。
DataFlow::Node的两个谓词,可以将数据流节点转Expr、Parameter的形式

  1. class Node {
  2. // 获取数据流节点Expr的形式
  3. Expr asExpr() { ... }
  4. // 获取数据流节点Parameter的形式
  5. Parameter asParameter() { ... }
  6. }

DataFlow的两个谓词,可以将Expr、Parameter转数据流节点的形式

  1. // 获取Expr对应的数据流节点形式
  2. ExprNode DataFlow::exprNode(Expr e)
  1. // 获取Parameter对应的数据流节点形式
  2. ParameterNode DataFlow::parameter(Parameter p)

1.1 使用本地数据流

可以通过DataFlow的谓词localFlowStep限定从nodeFrom流向nodeTo的数据流

  1. DataFlow::localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)

localFlowStep可以使用递归操作符+和,DataFlow的谓词localFlow相当于localFlowStep

代码示例

  1. import java
  2. import semmle.code.java.dataflow.DataFlow
  3. from Parameter source, Expr sink
  4. where DataFlow::localFlow(DataFlow::parameterNode(source), DataFlow::exprNode(sink))
  5. select source,sink

匹配结果示例

image.png

1.2 使用本地污点追踪

关于污点追踪:如下示例代码中,如果x被定义为污点,那么y也将是污点。

  1. String temp = x;
  2. String y = temp + ", " + temp;

本地污点追踪库位于semmle.code.java.dataflow.TaintTracking之中,需要手动导入。
类似本地数据流分析,可以使用TaintTracking的谓词localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)限定从nodeFrom流向nodeTo的数据流。
localTaintStep可以使用递归操作符+和,localTaint等价于localTaintStep

  1. TaintTracking::localTaint(DataFlow::parameterNode(source), DataFlow::exprNode(sink))

代码示例

  1. import java
  2. import semmle.code.java.dataflow.DataFlow
  3. import semmle.code.java.dataflow.TaintTracking
  4. from Parameter source, Expr sink
  5. where TaintTracking::localTaint(DataFlow::parameterNode(source), DataFlow::exprNode(sink))
  6. select source,sink

匹配结果示例

image.png

1.3 案例

限定sink是java.io.FileReader的第一个参数,利用本地数据流分析寻找所有的source

  1. import java
  2. import semmle.code.java.dataflow.DataFlow
  3. from Constructor fileReader, Call call, Parameter p
  4. where
  5. fileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") and
  6. call.getCallee() = fileReader and
  7. DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(0)))
  8. select p

匹配结果示例

image.png

二、全局数据流

全局数据流比本地数据流更强大,但是执行时也更消耗时间与内存。

2.1 使用全局数据流

需要继承DataFlow::Configuration

  1. import semmle.code.java.dataflow.DataFlow
  2. class MyDataFlowConfiguration extends DataFlow::Configuration {
  3. MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" }
  4. override predicate isSource(DataFlow::Node source) {
  5. ...
  6. }
  7. override predicate isSink(DataFlow::Node sink) {
  8. ...
  9. }
  10. }

Configuration内置的几个谓词

  • isSource—定义数据流的来源
  • isSink—定义数据流的终点
  • isBarrier—可选,限制数据流
  • isAdditionalFlowStep—可选,限制数据流的步数

然后通过调用Configuration的谓词hashFlow(DataFlow::Node source, DataFlow::Node sink)来执行数据流分析。

  1. from MyDataFlowConfiguration dataflow, DataFlow::Node source, DataFlow::Node sink
  2. where dataflow.hasFlow(source, sink)
  3. select source, "Data flow to $@.", sink, sink.toString()

2.2 使用全局污点追踪

https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll/type.TaintTrackingImpl$Configuration.html

需要继承TaintTracking::Configuration

  1. import semmle.code.java.dataflow.TaintTracking
  2. class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
  3. MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }
  4. override predicate isSource(DataFlow::Node source) {
  5. ...
  6. }
  7. override predicate isSink(DataFlow::Node sink) {
  8. ...
  9. }
  10. }

These predicates are defined in the configuration:

  • isSource—defines where taint may flow from
  • isSink—defines where taint may flow to
  • isSanitizer—optional, restricts the taint flow
  • isAdditionalTaintStep—optional, adds additional taint steps

全局污点追踪的执行与全局数据流的执行相似,也是通过调用Configuration的hasFlow谓词。

三、内置的数据流来源

Java库中内置了一些数据流源。
例如RemoteFlowSource类(需要导入包semmle.code.java.dataflow.FlowSources)限制了数据流源来自远端的用户。

代码示例1

  1. import java
  2. import semmle.code.java.dataflow.FlowSources
  3. from RemoteFlowSource source
  4. select source

匹配结果示例1

request.getHeader(“x-requested-with”);
request.getRequestURL();
req.getParameter(paramName);
image.png

代码示例2

远端数据流来源 + 全局污点追踪

  1. import java
  2. import semmle.code.java.dataflow.DataFlow
  3. import semmle.code.java.dataflow.FlowSources
  4. import semmle.code.java.dataflow.TaintTracking
  5. class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
  6. MyTaintTrackingConfiguration() {
  7. this = "MyTaintTrackingConfiguration"
  8. }
  9. override predicate isSource(DataFlow::Node source) {
  10. source instanceof RemoteFlowSource
  11. }
  12. override predicate isSink(DataFlow::Node sink) {
  13. exists(Call call |
  14. sink.asExpr() = call.getArgument(0)
  15. )
  16. }
  17. }
  18. from DataFlow::Node src, DataFlow::Node sink, TaintTracking::Configuration config
  19. where config.hasFlow(src, sink)
  20. select src, sink

匹配结果示例2

image.png