4. SAST 实战
首次 SAST 扫描的目标不是“把工具跑起来”,而是建立一条可复现的扫描基线:明确扫什么、用什么规则、排除什么、结果如何判读。下面以一个典型的 Spring Boot 多模块 Maven 项目为例,走完从配置到报告解读的完整过程。示例中的路径、规则 ID 和告警数量均为教学假设,用于说明判断逻辑,不代表任何真实项目的实测结果。
一、确定扫描目标与项目结构
假设项目结构如下:
spring-demo/
├── pom.xml
├── demo-web/ # Controller、DTO
├── demo-service/ # 业务逻辑、事务
├── demo-dao/ # MyBatis Mapper、SQL
├── demo-common/ # 工具类、常量
└── src/test/ # 单元测试
扫描目标应覆盖全部生产源码目录(demo-web、demo-service、demo-dao、demo-common),但不应把 src/test、target/、generated-sources/ 纳入。原因很直接:测试代码中的硬编码口令、target 中的编译产物和生成代码,都会制造大量无意义告警,稀释真正需要修复的问题。
二、规则集选择:先窄后宽
首次扫描不建议直接启用全部规则。推荐先启用与 Java/Spring 强相关的高置信度规则集,例如:
- 注入类:SQL 注入、命令注入、SpEL 表达式注入、LDAP 注入;
- Web 安全类:XSS、CSRF、开放重定向、路径穿越;
- 敏感信息类:硬编码密钥、明文密码、日志泄露敏感字段;
- 反序列化与组件类:不安全的
ObjectInputStream、已知漏洞依赖。
规则集过宽会带来大量低价值告警,过窄则可能漏掉真实缺陷。首次扫描后,再根据误报率逐步调整规则开关和严重级别阈值。
三、排除路径配置
排除路径要写在扫描配置中,而不是靠人工过滤报告。典型排除项包括:
exclude:
- "**/target/**"
- "**/generated-sources/**"
- "**/src/test/**"
- "**/*.min.js"
- "**/node_modules/**"
注意:demo-common 中的工具类不要整体排除,因为其中可能包含加密、序列化等高风险逻辑。排除应针对“非生产、非手写、非运行时代码”,而不是针对“看起来不重要的模块”。
四、执行扫描与报告解读
假设扫描完成后,报告给出 37 条告警,按严重级别分布为:高危 6 条、中危 14 条、低危 17 条。此时不要直接按数量排序修复,而要先做三件事:
- 看数据流:告警是否给出了从 source 到 sink 的完整路径。例如
@RequestParam接收的参数是否未经校验就拼进 SQL。 - 看上下文:同一段代码是否已有全局过滤器、参数化查询或白名单校验。
- 看可达性:该接口是否对外暴露、是否需要认证、是否在真实调用链上。
下面这张图展示了一条典型 SQL 注入告警从输入到判定的完整信号链,以及误报与漏报的分叉点:
五、误报与漏报的处理原则
误报不是“工具错了”就结束。每条误报都应记录:告警 ID、判定理由、忽略依据(如已有全局过滤器、参数化封装、内部常量来源)。这样下次扫描时才能区分“新出现的误报”和“已知误报”。
漏报更危险,因为它不会出现在报告里。常见漏报来源包括:
- 规则未覆盖框架特有写法,例如 MyBatis 的
${}拼接; - 污点追踪在跨模块、跨线程或反射调用处中断;
- 扫描目标排除了实际参与运行的模块或生成代码。
发现漏报后,应优先补充自定义规则或调整污点传播配置,而不是仅靠人工审计兜底。人工审计可以作为补充,但不能替代规则覆盖。
六、首次扫描的可交付成果
一次合格的首次扫描,至少应产出以下内容:
| 交付项 | 内容要求 | 示例 |
|---|---|---|
| 扫描配置 | 目标路径、规则集、排除路径 | 4 个生产模块,启用注入/Web/敏感信息规则 |
| 告警清单 | 按严重级别 + 可达性排序 | 高危 6 条,其中 4 条确认为真实缺陷 |
| 误报记录 | 告警 ID + 忽略理由 | 2 条因已有参数化封装而忽略 |
| 漏报线索 | 未覆盖的写法或路径 | MyBatis ${} 需补充规则 |
| 修复优先级 | 按可达性 + 影响面排序 | 先修对外接口的 SQL 注入 |