CodeQL从入门到放弃
字数 1375 2025-08-13 21:33:17

CodeQL 从入门到实战:自动化代码审计指南

一、CodeQL 概述

1.1 什么是 CodeQL

CodeQL 是 GitHub 推出的代码自动化审计工具,它能够:

  • 将源代码转换为可查询的中间层数据库(AST 结构)
  • 通过编写 QL 查询语句来检测代码中的安全漏洞
  • 支持多种语言(Java、C/C++、Python、JavaScript 等)

1.2 CodeQL 工作原理

  1. 数据库生成:将源代码编译并转换为 CodeQL 可识别的数据库
  2. 规则编写:使用 QL 语言编写检测规则
  3. 查询执行:在数据库上运行 QL 查询,发现潜在漏洞

1.3 适用场景

  • 代码安全审计
  • 漏洞自动化检测
  • 安全研究(注意:禁止用于企业 CI/CD 流程)

二、环境搭建

2.1 安装准备

系统要求

  • 操作系统:Mac/Linux/Windows
  • Java JDK 1.8+
  • Maven 3.6.3+(Java 项目需要)

2.2 安装步骤

  1. 安装 CodeQL 引擎

    # 下载引擎
    wget https://github.com/github/codeql-cli-binaries/releases/download/v2.4.0/codeql-linux64.zip
    unzip codeql-linux64.zip -d ~/CodeQL/codeql
    
    # 添加环境变量
    export PATH=$PATH:~/CodeQL/codeql
    
  2. 安装 SDK

    cd ~/CodeQL
    git clone https://github.com/Semmle/ql
    
  3. 安装 VSCode 插件

    • 安装 Visual Studio Code
    • 在扩展中搜索并安装 "CodeQL" 插件
    • 配置 CodeQL 引擎路径:~/CodeQL/codeql

三、基础使用

3.1 创建数据库

对于 Java 项目:

codeql database create ~/CodeQL/databases/micro-service-seclab-database \
  --language="java" \
  --command="mvn clean install --file pom.xml" \
  --source-root=~/CodeQL/micro-service-seclab/

参数说明:

  • --language:项目语言
  • --command:编译命令(脚本语言不需要)
  • --source-root:项目根目录

3.2 基本查询

示例:Hello World 查询

import java

from int i
where i = 1
select i

3.3 QL 基本语法

查询结构

from [datatype] var
where condition(var = something)
select var

常用类库

  • Method:方法类
  • MethodAccess:方法调用类
  • Parameter:参数类

示例:查询所有方法

import java

from Method method
select method

四、漏洞检测实战

4.1 三元组概念

  • Source:污染源(如 HTTP 请求参数)
  • Sink:危险函数调用点(如 SQL 执行函数)
  • Sanitizer:净化函数(阻断污染链)

4.2 SQL 注入检测

配置类

class VulConfig extends TaintTracking::Configuration {
  VulConfig() { this = "SqlInjectionConfig" }
  
  // 定义 Source
  override predicate isSource(DataFlow::Node src) {
    src instanceof RemoteFlowSource
  }
  
  // 定义 Sink
  override predicate isSink(DataFlow::Node sink) {
    exists(Method method, MethodAccess call |
      method.hasName("query") and
      call.getMethod() = method and
      sink.asExpr() = call.getArgument(0)
    )
  }
}

完整查询

/**
 * @id java/examples/vuldemo
 * @name Sql-Injection
 * @description Sql-Injection
 * @kind path-problem
 * @problem.severity warning
 */
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.QueryInjection
import DataFlow::PathGraph

class VulConfig extends TaintTracking::Configuration {
  VulConfig() { this = "SqlInjectionConfig" }
  
  override predicate isSource(DataFlow::Node src) {
    src instanceof RemoteFlowSource
  }
  
  override predicate isSink(DataFlow::Node sink) {
    exists(Method method, MethodAccess call |
      method.hasName("query") and
      call.getMethod() = method and
      sink.asExpr() = call.getArgument(0)
    )
  }
}

from VulConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "source"

4.3 误报处理

问题:基础类型参数被误报为漏洞

解决方案:添加 Sanitizer

override predicate isSanitizer(DataFlow::Node node) {
  node.getType() instanceof PrimitiveType or
  node.getType() instanceof BoxedType or
  node.getType() instanceof NumberType or
  exists(ParameterizedType pt|
    node.getType() = pt and
    pt.getTypeArgument(0) instanceof NumberType
  )
}

4.4 漏报处理

问题:Optional 类型参数未被检测

解决方案:添加 AdditionalTaintStep

predicate isTaintedString(Expr expSrc, Expr expDest) {
  exists(Method method, MethodAccess call, MethodAccess call1 |
    expSrc = call1.getArgument(0) and
    expDest=call and
    call.getMethod() = method and
    method.hasName("get") and
    method.getDeclaringType().toString() = "Optional<String>" and
    call1.getArgument(0).getType().toString() = "Optional<String>"
  )
}

override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
  isTaintedString(node1.asExpr(), node2.asExpr())
}

五、高级技巧

5.1 instanceof 优化

使用抽象类和 instanceof 简化复杂查询:

abstract class RemoteFlowSource extends DataFlow::Node {
  abstract string getSourceType();
}

override predicate isSource(DataFlow::Node src) {
  src instanceof RemoteFlowSource
}

5.2 递归查询

场景:查找嵌套类的最外层类

解决方案

from Class classes
where classes.getName().toString() = "innerTwo"
select classes.getEnclosingType+()  // 获取所有上级作用域

5.3 类型过滤

使用强制类型转换进行过滤:

from Parameter param
select param, param.getType().(RefType)  // 只保留引用类型

六、Lombok 问题处理

问题:Lombok 生成的代码无法被 CodeQL 识别

解决方案

# 下载 lombok.jar
wget https://projectlombok.org/downloads/lombok.jar -O "lombok.jar"

# 运行 delombok
java -jar "lombok.jar" delombok -n --onlyChanged . -d "delombok"

# 清理生成的文件
find "delombok" -name '*.java' -exec sed '/Generated by delombok/d' -i '{}' +
find "delombok" -name '*.java' -exec sed '/import lombok/d' -i '{}' +
cp -r "delombok/" .
rm -rf "delombok"

七、工程化应用

7.1 命令行集成

# 生成数据库
codeql database create ~/CodeQL/databases/micro-service-seclab \
  --language="java" \
  --command="mvn clean install --file pom.xml -Dmaven.test.skip=true" \
  --source-root="~/Code/micro-service-seclab/"

# 执行查询并输出结果
codeql database analyze /CodeQL/databases/micro-service-seclab \
  /CodeQL/ql/java/ql/examples/demo \
  --format=csv \
  --output=/CodeQL/Result/micro-service-seclab.csv \
  --rerun

7.2 最佳实践

  1. 结合商业 SAST 工具对比结果
  2. 定期更新 CodeQL 规则库
  3. 针对项目特点定制规则
  4. 建立误报/漏报反馈机制

八、总结

CodeQL 是一个强大的代码自动化审计工具,通过:

  1. 正确定义 Source 和 Sink
  2. 合理处理误报和漏报
  3. 掌握高级查询技巧
  4. 解决 Lombok 等特殊问题

可以有效地提高代码审计效率。建议在实际使用中结合商业工具,以获得更全面的检测覆盖。

参考资料

  1. CodeQL 官方文档
  2. GitHub CodeQL 仓库
  3. FreeBuf 原文
CodeQL 从入门到实战:自动化代码审计指南 一、CodeQL 概述 1.1 什么是 CodeQL CodeQL 是 GitHub 推出的代码自动化审计工具,它能够: 将源代码转换为可查询的中间层数据库(AST 结构) 通过编写 QL 查询语句来检测代码中的安全漏洞 支持多种语言(Java、C/C++、Python、JavaScript 等) 1.2 CodeQL 工作原理 数据库生成 :将源代码编译并转换为 CodeQL 可识别的数据库 规则编写 :使用 QL 语言编写检测规则 查询执行 :在数据库上运行 QL 查询,发现潜在漏洞 1.3 适用场景 代码安全审计 漏洞自动化检测 安全研究(注意:禁止用于企业 CI/CD 流程) 二、环境搭建 2.1 安装准备 系统要求 : 操作系统:Mac/Linux/Windows Java JDK 1.8+ Maven 3.6.3+(Java 项目需要) 2.2 安装步骤 安装 CodeQL 引擎 : 安装 SDK : 安装 VSCode 插件 : 安装 Visual Studio Code 在扩展中搜索并安装 "CodeQL" 插件 配置 CodeQL 引擎路径: ~/CodeQL/codeql 三、基础使用 3.1 创建数据库 对于 Java 项目: 参数说明: --language :项目语言 --command :编译命令(脚本语言不需要) --source-root :项目根目录 3.2 基本查询 示例:Hello World 查询 3.3 QL 基本语法 查询结构 : 常用类库 : Method :方法类 MethodAccess :方法调用类 Parameter :参数类 示例:查询所有方法 : 四、漏洞检测实战 4.1 三元组概念 Source :污染源(如 HTTP 请求参数) Sink :危险函数调用点(如 SQL 执行函数) Sanitizer :净化函数(阻断污染链) 4.2 SQL 注入检测 配置类 : 完整查询 : 4.3 误报处理 问题 :基础类型参数被误报为漏洞 解决方案 :添加 Sanitizer 4.4 漏报处理 问题 :Optional 类型参数未被检测 解决方案 :添加 AdditionalTaintStep 五、高级技巧 5.1 instanceof 优化 使用抽象类和 instanceof 简化复杂查询: 5.2 递归查询 场景 :查找嵌套类的最外层类 解决方案 : 5.3 类型过滤 使用强制类型转换进行过滤: 六、Lombok 问题处理 问题 :Lombok 生成的代码无法被 CodeQL 识别 解决方案 : 七、工程化应用 7.1 命令行集成 7.2 最佳实践 结合商业 SAST 工具对比结果 定期更新 CodeQL 规则库 针对项目特点定制规则 建立误报/漏报反馈机制 八、总结 CodeQL 是一个强大的代码自动化审计工具,通过: 正确定义 Source 和 Sink 合理处理误报和漏报 掌握高级查询技巧 解决 Lombok 等特殊问题 可以有效地提高代码审计效率。建议在实际使用中结合商业工具,以获得更全面的检测覆盖。 参考资料 CodeQL 官方文档 GitHub CodeQL 仓库 FreeBuf 原文