GabrielBBaldez/spring-taint

GitHub: GabrielBBaldez/spring-taint

基于 Tai-e 的 Spring Boot 过程间污点分析工具,专门检测 SonarQube 等传统工具无法覆盖的跨层、跨服务数据流注入漏洞。

Stars: 0 | Forks: 0

Spring Taint Analyzer

# Spring Taint Analyzer [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/e21751db93231225.svg)](https://github.com/GabrielBBaldez/spring-taint/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/GabrielBBaldez/spring-taint?sort=semver)](https://github.com/GabrielBBaldez/spring-taint/releases) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Java](https://img.shields.io/badge/Java-17%2B-orange.svg)](#building) 检测跨 **7 个框架**的 **12 种漏洞类别**,包括跨层、 响应式、跨服务和跨请求的存储型注入——**37 个漏洞基准案例中检测出 36 个,且 0 误报**(差一点缺失层捕获了最后一个,并标记了*已尝试但不正确*的过滤)。以 CLI、独立 jar 包、 Docker 镜像和带有 SARIF 2.1 输出的 GitHub Action 形式发布。 ## 问题所在 考虑以下看似无害的 Spring Boot 代码: ``` // Controller @GetMapping("/users") public List search(@RequestParam String name) { return userService.search(name); } // Service public List search(String name) { String filtered = nameFilter(name); // looks like sanitization, but isn't return userRepo.findByName(filtered); } // Repository public List findByName(String name) { return jdbc.query( "SELECT * FROM users WHERE name = '" + name + "'", // 🚨 SQL Injection mapper ); } ``` 该值来自 `@RequestParam`,穿过 service 和 repository 层,未经任何过滤就到达了 SQL 查询。像 `name = ' OR '1'='1` 这样简单的 payload 就会暴露整个表。 **SonarQube 无法检测到此路径。** 它只会标记 sink 和 source 在同一方法中的情况。真正的漏洞存在于跨多个层的流转中——而这正是本项目所致力于解决的问题。 ## 什么是污点分析 污点分析使用三个概念来追踪不受信任数据在系统中的流转: ``` [SOURCE] ──► data flow ──► [SANITIZER?] ──► [SINK] │ if absent → alert ``` - **Source** —— 外部数据进入的地方:`@RequestParam`、`@RequestBody`、`@KafkaListener` - **Sanitizer** —— 负责净化数据的机制:`HtmlUtils.htmlEscape()`、参数化查询、`@Valid` - **Sink** —— 危险数据被消费的地方:`JdbcTemplate.execute()`、`Runtime.exec()`、`response.write()` 如果数据从 source 流向 sink **且未经过 sanitizer** → 潜在漏洞。 该分析是**过程间**的:它跨方法、类和抽象层追踪数据——而不仅仅是在单个函数内。 ## 定位:SonarQube 的补充 本项目**不**取代 SonarQube。它们服务于不同的目的: | 工具 | 目的 | 过程间污点分析 | |---|---|---| | SonarQube | 通用质量 + bug + 简单漏洞 | ❌ | | Semgrep OSS | 静态代码模式 | ❌ | | Semgrep Pro | 过程间污点分析 | ✅ — 但是**收费** | | Checkmarx / Veracode | 完整的企业级 SAST | ✅ — 但是**昂贵** | | **Spring Taint Analyzer** | 针对 Spring Boot 的过程间污点分析 | ✅ — **免费** | 实际应用中的不同之处——依赖于真正的过程间污点分析的 Spring 特定功能: | 功能 | Spring Taint | SonarQube (免费) | Semgrep OSS | |---|:---:|:---:|:---:| | 过程间污点分析(跨方法/层) | ✅ | ❌ | ❌ | | `@KafkaListener` / `@FeignClient` 作为 source | ✅ | ❌ | ❌ | | `MultipartFile` / `@MatrixVariable` 作为 source | ✅ | ❌ | ❌ | | 条件 sanitizer | ✅ | ❌ | ❌ | | 跨请求存储型注入 | ✅ | ❌ | ❌ | | WebFlux / Reactor (`Mono` / `Flux`) | ✅ | ❌ | ❌ | | JPQL / 模板 / JNDI / XXE 注入 | ✅ | ❌ | ❌ | | Spring Security & `application.yml` 配置错误 | ✅ | ❌ | ❌ | | 差点缺失 sanitizer 检测(错误/不足的过滤) | ✅ | ❌ | ❌ | | 自动修复 —— 应用修复(参数化查询 / 输出转义) | ✅ | ❌ | ❌ | | 每个发现项的置信度评分 | ✅ | ❌ | ❌ | | 用于拉取请求的 Diff 模式 + 基线 | ✅ | ❌ | 部分 | | SARIF 2.1 输出 | ✅ | ✅ | ✅ | | 免费 / 开源 | ✅ | ✅ | ✅ | 在 CI pipeline 中的预期用途: ``` - sonarqube scan # general quality, code smells, coverage - spring-taint scan # deep data-flow vulnerabilities ``` **一句话价值主张:** 你已经在使用 SonarQube——而本项目能检测到它所看不见的内容。 ## 架构 基于 **[Tai-e](https://github.com/pascal-lab/Tai-e)**(南京大学,ISSTA 2023)构建,这是一个现代的 Java 静态分析框架。Tai-e 解决了其中的难点——调用图构建、上下文敏感的指针分析以及过程间 **IFDS** 污点传播。我们的工作则是建立在其之上的 Spring 层。 ``` Spring Boot project │ compile (Maven / Gradle) ▼ Bytecode (.class / JAR) ← analysis runs here, not on source │ ▼ Tai-e: Call Graph + Pointer Analysis │ ▼ IFDS Taint Propagation │ ▼ Spring Source/Sink Config ← our differentiator (@RequestParam, @KafkaListener, JdbcTemplate, Runtime.exec…) │ ▼ SARIF 2.1 report (terminal / GitHub / GitLab / VS Code) ``` 直接在 bytecode(而非源代码)上运行,能提供精确的继承/泛型解析,无需源码即可分析第三方依赖,并独立于任何 IDE 或构建系统。 ## 项目结构 ``` spring-taint/ ├── config/ │ └── spring-taint.yml # default Spring sources/sinks/sanitizers (Tai-e format) ├── docs/ │ └── design/ # technical scope & design notes ├── spring-taint-engine/ # analyzer: CLI, config loader, Tai-e adapter, SARIF reporter └── spring-taint-benchmark/ # intentionally vulnerable Spring Boot cases + ground truth ``` ## 基准测试 就像 FlowDroid 的 DroidBench 一样,本仓库内置了一个基准测试,包含故意设计为有漏洞(和安全)的 Spring Boot 案例。每一个对外宣称的检测功能在发布前都会针对它进行验证。 该基准测试包含 **40 个案例(37 个有漏洞,3 个安全)**,覆盖 SQL 和 JPQL 注入 (直接的、穿透 service 层的、四层调用、通过 Kafka 和 RabbitMQ、响应式 R2DBC)、反射型、 条件 sanitizer 和 **跨请求存储型** XSS、SSRF、SpEL、JNDI、XXE、 模板注入 (SSTI)、日志注入、路径遍历、命令注入和 开放重定向——其 source 来自 Spring (`@RequestParam`、`@PathVariable`、 `@RequestBody`、`@RequestHeader`、`@MatrixVariable`、`MultipartFile`)、 `@KafkaListener`、`@RabbitListener`、JAX-RS (`@QueryParam`)、`@Repository` 读取、 **`@FeignClient` 结果、`@Scheduled` 任务和 `@Transactional` 先写后读**, 此外还有通过 `Optional` / `CompletableFuture` 包装器流转的污点。基准真值见 [`expected.yml`](spring-taint-benchmark/expected.yml)。 当前引擎结果:**仅靠污点引擎就检测出了 37 个漏洞案例中的 36 个, ** 在 3 个安全案例上 **0 误报**;差点缺失层 (`--src`) 捕获了剩余的上下文错误的流 (37) 并解释了其余部分。完整表格:[基准测试 README](spring-taint-benchmark/README.md)。 每条规则参考:[docs/rules.md](docs/rules.md)。 正面案例衡量**召回率**;安全案例衡量**精确率**。 除了合成基准测试外,分析器还在真实的 OSS 应用上运行,覆盖 Spring Boot 3 (`jakarta`) 和 Spring Boot 2 (`javax`): - **spring-petclinic**(干净,Boot 4.0)—— 正确接入(9 个入口点,12 个 source)并报告了 **0 误报**,外加一个项目自身标记为生产环境不安全的合法配置发现。 - **spring-petclinic-rest**(干净,更大规模 —— 约 126 个类,真实的 `JdbcTemplate` 用法) —— 在大规模下 **0 误报**(31 个入口点,46 个 source;分析耗时约 0.2 秒)。 - **sql-injection-web**(有漏洞)—— 以 99% 的置信度发现了**跨层** SQL 注入 (controller → repository,两个文件)并生成了修复程序。 - **Contrast vulnerable-spring-boot-application**(有漏洞,Boot 2 / `javax`,通过 `@RequestParam Map` 传值) —— 以 99% 的置信度发现了跨层 SQL 注入。 详见 [docs/validation.md](docs/validation.md)。 ## 其检测类别对应的真实 CVE 基准测试证明了在合成案例上的召回率;这些是野外**相同漏洞类别的公开 CVE**。 分析器对应用程序 bytecode 进行推理,并报告每个流转的过程间 source-to-sink 形式——因此,当易受攻击的调用位于被分析的代码中,而不仅仅是在第三方库内部时,它就能捕获这些模式。 | 类别 | 检测器 | 具代表性的公开 CVE | 数据流 | |---|---|---|---| | SQL 注入 (CWE-89) | `sql-injection` | [CVE-2020-5427 / CVE-2020-5428](https://spring.io/security/cve-2020-5428/) — Spring Cloud Data Flow / Task | 请求可控的 `sort` 列被拼接到 task-execution 查询中 | | SQL 注入 (CWE-89) | `sql-injection` | [CVE-2016-6652](https://spring.io/security/cve-2016-6652/) — Spring Data JPA | 来自请求的 `Sort` 值到达生成的 SQL(盲注 SQLi) | | SQL 注入 (CWE-89) | `sql-injection` | [CVE-2024-54762](https://nvd.nist.gov/vuln/detail/CVE-2024-54762) — RuoYi (Spring Boot admin) | 已认证的请求参数未经过滤到达查询 | | SpEL 注入 (CWE-917) | `spel-injection` | [CVE-2018-1273](https://nvd.nist.gov/vuln/detail/CVE-2018-1273) — Spring Data Commons | 构造的请求 payload 属性路径被作为 SpEL 表达式执行 | 每一个都是请求值跨方法流入中间没有 sanitizer 的 sink——这正是此工具追踪的形态。 (对于易受攻击的调用位于框架而非应用程序中的 CVE,只有当从被分析的代码到达该调用时,分析器才会报告它。) ## 各阶段范围 - **阶段 1 —— Spring MVC (MVP):** SQL 注入、XSS、路径遍历、命令注入、SSRF、SpEL 注入、开放重定向。Source:`@RequestParam`、`@PathVariable`、`@RequestBody`、`@RequestHeader`、`@CookieValue`、`@ModelAttribute`、servlet API。退出标准:检测到每个基准案例,零漏报且精确率 > 80%。 - **阶段 2 —— 现有 OSS 工具留下的空白(已完成):** `@KafkaListener` 和 `@RabbitListener` 作为 source,条件 sanitizer,自定义方法 sanitizer,跨请求存储型注入,WebFlux / 异步(`Mono`/`Flux` 作为透明的污点包装器)。 - **阶段 3 —— 多框架与健壮性(已完成):** JAX-RS / Quarkus 和 Micronaut source;JNDI / XXE / 模板 / JPQL / 日志注入 sink;`@FeignClient`、`@Scheduled` 和 `@Transactional` source;配置和错误配置审计。 - **阶段 4 —— 路线图:** gRPC source、IntelliJ 插件,以及将镜像发布到 GHCR。 完整的技术范围位于 [`docs/design/spring-taint-scope.md`](docs/design/spring-taint-scope.md)。 ## 用法 命令(可运行的 `java -jar …/spring-taint-all.jar` 形式见 [构建](#building);`scan` 需要通过 `--libs` 传入目标的依赖 classpath): ``` # 基础扫描 spring-taint scan target/classes --libs "" # 自定义配置(合并到内置规则上) spring-taint scan target/classes --libs "…" --config spring-taint.yml # SARIF 输出(GitHub Advanced Security、GitLab SAST、VS Code) spring-taint scan target/classes --libs "…" --output results.sarif # 按严重性过滤,显示完整 trace spring-taint scan target/classes --libs "…" --severity critical,high --verbose # 仅显示与 base ref 相比改动过的文件的 findings(快速 PR 扫描) spring-taint scan target/classes --libs "…" --diff origin/main # Near-miss 提示 + 建议的 parameterized-query 修复(需要 sources) spring-taint scan target/classes --libs "…" --src src/main/java --suggest-fixes # 将高可信度的修复应用到 source spring-taint scan target/classes --libs "…" --src src/main/java --fix # 在遗留 codebase 上采用:记录今天的 findings,然后仅在出现 NEW findings 时失败 spring-taint scan target/classes --libs "…" --baseline spring-taint-baseline.txt ``` 示例输出(每个污点发现项都带有置信度评分): ``` [CRITICAL] sql-injection (confidence: 95%) Source: UserController.java:28 - search() - tainted parameter Flow: UserController.search() -> UserService.search() -> UserRepository.query() Sink: UserRepository.java:27 - sink: query() Sanitizer: none detected ``` ## 可扩展性 团队可以按 Tai-e 的 YAML 格式添加自己的规则: ``` sources: - { kind: call, method: "", index: result } - { kind: param, method: "", index: 0 } sinks: - { method: "", index: 0 } sanitizers: - { method: "", index: 0 } ``` ## 构建 需要 JDK 17+ 和 Maven。 ``` mvn -q clean package # build engine + benchmark mvn -q -pl spring-taint-benchmark package # compile the benchmark cases only ``` ### 运行扫描 构建独立的 jar 包并扫描已编译的类。通过 `--libs` 传入目标的 依赖 classpath,以便像 `JdbcTemplate` 这样的框架类型能够解析 (内置了污点配置,因此 `--config` 是可选的): ``` java -jar spring-taint-engine/target/spring-taint-all.jar \ scan target/classes --libs "$(... your dependency classpath ...)" \ --output results.sarif ``` 自定义的 `--config` 会**合并**到内置规则上(使用 `--no-default-config` 来替换它们)。 ### 密钥、配置与错误配置扫描 三种基于模式的扫描(任意 JDK,无需污点引擎)补充了污点分析: ``` # bytecode 中的硬编码 secrets — 以 secret 命名的常量、已知 key 格式 # (AWS、GitHub, …)以及 @Value 默认值 java -jar …/spring-taint-all.jar secrets target/classes # application*.yml / .properties 中的不安全设置 — 硬编码 secrets、 # 已禁用的 TLS、已排除的 Security auto-config、Actuator "*"、H2 console java -jar …/spring-taint-all.jar config src/main/resources # bytecode 中不安全的 Spring 代码 — csrf()/frameOptions().disable()、 # @CrossOrigin("*")、不安全的 cookies、被记录的敏感数据 java -jar …/spring-taint-all.jar misconfig target/classes ``` ## GitHub Action 在 CI 中运行分析器并将发现项上传到 GitHub 代码扫描。该 Action 作为 Docker 容器在 JDK 17 上运行;为其提供已编译的类和 依赖 classpath: ``` - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: { distribution: temurin, java-version: '17' } - run: mvn -B -ntp package -DskipTests - id: cp run: echo "value=$(mvn -q dependency:build-classpath -Dmdep.outputFilterFile=/dev/stdout)" >> "$GITHUB_OUTPUT" - name: Spring Taint Analysis uses: GabrielBBaldez/spring-taint@main with: path: target/classes libs: ${{ steps.cp.outputs.value }} output: results.sarif severity: critical,high - uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: results.sarif ``` 所有输入项详见 [`action.yml`](action.yml)。本仓库还会在每次推送时扫描自身的 基准测试 —— 详见 [`.github/workflows/ci.yml`](.github/workflows/ci.yml)。 ### 拉取请求审查 [`examples/pr-security.yml`](examples/pr-security.yml) 是一个为你自己的项目准备的直接复制粘贴 workflow: 在每次 PR 时,它只扫描更改的代码 (`--diff`),上传 SARIF,以便发现**内联在 PR 中显示**(GitHub 代码扫描),并将 **建议的修复**(参数化查询 / 输出转义)作为 PR 评论发布。将它与 `--baseline` 配对,以仅对新引入的问题进行拦截。 ## Dashboard 一个 Web 控制台(React + Vite + TypeScript)将 SARIF 输出可视化:严重程度 明细、按规则分类的发现项,以及每个发现项的完整 **source → sink 污点流**。 拖放一个 `.sarif` 文件即可加载你自己的报告。详见 [`dashboard/`](dashboard/)。 ``` cd dashboard && npm install && npm run dev # → http://localhost:4321 ``` ## 状态 - [x] 范围与定位 - [x] 引擎选择 - [x] 与竞品的空白映射 - [x] 项目脚手架(Maven 多模块、CLI 骨架、配置加载器、SARIF 模型) - [x] 初始基准测试:SQL 注入(直接 / 穿透 service 层 / 通过 Kafka / 安全)、反射型 XSS、路径遍历、命令注入 - [x] 引擎:在基准测试上端到端打通 Tai-e IFDS - [x] Spring source 层:注解 → Tai-e param-source 生成 - [x] 带有 SARIF 输出的功能性 CLI - [x] 当前基准测试上的精确率/召回率 —— **检测出 30/30 个漏洞案例,0 误报**,覆盖 SQL / JPQL 注入(直接 / 穿透 service 层 / 四层 / 通过 Kafka / 响应式 R2DBC)、反射型 / 条件型 / **跨请求存储型** XSS、SSRF、SpEL、JNDI、XXE、模板注入 (SSTI)、日志注入、路径遍历、命令注入、开放重定向;多框架 source(Spring MVC/WebFlux、Kafka、JAX-RS/Quarkus、Micronaut、`@Repository` 读取、`@FeignClient`、`@Scheduled`) - [x] 阶段 2 差异化:`@KafkaListener` source、条件 sanitizer、存储型 / 二阶注入 - [x] GitHub Action (Docker) + 独立 jar 包 + CI workflow - [x] 用于 SARIF 报告的 Web Dashboard (React + Vite) - [x] 硬编码密钥扫描器(`secrets` 命令);可合并的 `--config` - [x] 健壮性检查:JNDI / XXE / 日志 / 模板 / JPQL sink,文件上传和 `@MatrixVariable` source,`Optional` / `CompletableFuture` 污点转移,框架内部 sink 过滤 - [x] 配置与错误配置审计:`config`(不安全的 `application.yml`/`.properties`)和 `misconfig`(CSRF/点击劫持被禁用、CORS `*`、不安全的 cookie、记录敏感数据) - [x] 采用度:每个发现项的置信度评分(控制台 + SARIF),用于快速拉取请求扫描的 `scan --diff `,内联 `// spring-taint: suppress` 注释(`--src` / `suppressions`),以及用于捕获拼写错误的自定义规则的 `validate-config` - [x] 高级 source:`@FeignClient` 结果(跨服务),`@Scheduled` 任务作为入口点,以及 `@Transactional` 先写后读存储型注入 - [x] 差点缺失 sanitizer(`--src`):标记不足(去除引号)、黑名单、丢弃结果以及上下文错误的过滤 —— 即“我确信这是安全的”这类 bug - [x] 自动修复(`--suggest-fixes` / `--fix`):将拼接的 SQL 查询重写为参数化查询;已完成端到端验证(应用修复后,基准测试的 SQL 发现项从 15 降至 1,且修补后的代码可以编译) - [x] 扫描器的单元测试套件(18 个测试,带有回归覆盖);bytecode 扫描器(`secrets`/`misconfig`)读取任意 JDK 的类文件 (ASM 9.7) - [x] 自动修复涵盖 XSS(包装在 `HtmlUtils.htmlEscape` 中)以及 SQL;采用基线模式(`--baseline`)以便在旧代码库中采用,并仅对新的发现项拦截 CI ## 已知限制 静态分析具有固有的局限性。对于本项目: - **Java 反射**(`Class.forName()`、`Method.invoke()`)可能会打断流 - **Spring 动态代理**(AOP / CGLib)引入的间接性可能会破坏调用图 - **Entity / DTO 字段追踪** —— source 仅限 `String`(如果 `@Repository`/`@FeignClient` 返回的 DTO,其 getter 随后被读取,则不会被继续追踪),这是一种重精确率轻召回率的选择 - **复杂的 Lambda / 方法引用** —— 通过 Tai-e 实现部分覆盖 - **污点分析运行在 JDK 17 上** —— Tai-e 0.5.1 无法读取 JDK 21 的 bytecode 每个版本都明确记录了其局限性,以及执行这些限制的测试用例。 ## 致谢 基于 [Tai-e](https://github.com/pascal-lab/Tai-e)(南京大学)构建,它 提供了调用图构建、指针分析和 IFDS 污点传播。 Tai-e 基于 LGPL-3.0 授权;本项目将其作为库依赖。 ## 许可证 [MIT](LICENSE) © Gabriel Baldez。
标签:CISA项目, JS文件枚举, SAST, Spring Boot, 云安全监控, 后台面板检测, 域名枚举, 盲注攻击, 请求拦截, 静态分析