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 工作原理
- 数据库生成:将源代码编译并转换为 CodeQL 可识别的数据库
- 规则编写:使用 QL 语言编写检测规则
- 查询执行:在数据库上运行 QL 查询,发现潜在漏洞
1.3 适用场景
- 代码安全审计
- 漏洞自动化检测
- 安全研究(注意:禁止用于企业 CI/CD 流程)
二、环境搭建
2.1 安装准备
系统要求:
- 操作系统:Mac/Linux/Windows
- Java JDK 1.8+
- Maven 3.6.3+(Java 项目需要)
2.2 安装步骤
-
安装 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 -
安装 SDK:
cd ~/CodeQL git clone https://github.com/Semmle/ql -
安装 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 最佳实践
- 结合商业 SAST 工具对比结果
- 定期更新 CodeQL 规则库
- 针对项目特点定制规则
- 建立误报/漏报反馈机制
八、总结
CodeQL 是一个强大的代码自动化审计工具,通过:
- 正确定义 Source 和 Sink
- 合理处理误报和漏报
- 掌握高级查询技巧
- 解决 Lombok 等特殊问题
可以有效地提高代码审计效率。建议在实际使用中结合商业工具,以获得更全面的检测覆盖。