扫描器报了 100 条 High,我先做了什么?

26 阅读5分钟

结果先说:

自动扫描:100 条
人工确认:没有一条能直接作为真实漏洞交付
人工复核:另外发现 2 个 P1 和 4 类中低风险

测试对象是一个真实的三节点 Go KV 项目,不是故意写坏的示例仓库。

扫描命令:

code-audit . \
  --format json \
  --output runproof-kv-scan.json \
  --with-metadata \
  --ignore "data/**" \
  --ignore "dist/**" \
  --ignore "docs/**" \
  --ignore "**/*_test.go"

扫描摘要:

规则数量
ruby-rce79
sql-concat13
unvalidated-file-path8
合计100

如果从第 1 条开始修,大概率会把 100 条全部当真实漏洞。

我处理这批结果时只做了 4 步。

cover-掘金-100条High-1920x1080.png

第 1 步:先按规则和文件分组

不要先看严重度,也先不要把结果交给别人。

先按两个字段分组:

pattern + file

最后得到的是三组结果,而不是 100 个独立问题。再抽每组第一条看代码,判断这批结果是不是同一个原因造成的。

这一步的目标只有一个:把“100 条告警”压缩成“几个需要确认的原因”。

第 2 步:先看语法是不是属于该规则的语言

这个 Go 项目的扫描结果里,最多的 ruby-rce 并不是 Ruby 代码。

原因是扫描规则把以下写法误认定为 Ruby 命令替换:

  • Go 反引号原始字符串;
  • JavaScript 模板字符串;
  • 普通代码片段里的反引号。

sql-concat 里也出现了类似问题:

Delete
deleteHandler

规则看到了类似 SQL 关键词的文本,但代码并没有把用户输入拼进 SQL。

unvalidated-file-path 则把 path/filepath 导入和路由参数当作文件读取。

第一轮确认可以先回答一句话:

这段语法真的是当前规则面对的语言和执行方式吗?

答案是否定时,先标成“语言或语法误报”,不要继续按真实漏洞排期。

第 3 步:真正可疑的结果,追到执行入口

误报不代表全部结果都能跳过。

对剩下的结果,只追三件事:

  1. 输入是否来自用户、文件、网络或外部系统;
  2. 输入能不能到达危险执行入口;
  3. 中间是否存在参数化、转义、校验或 allowlist。

例如一条 sql-concat,如果最后走到:

request -> sql string -> db.execute

才需要按真实问题处理。

如果路径是:

函数名包含 delete -> 规则匹配

那它只是一次文本命中。

这一步完成后,给每条结果只留一个结论:

真实可达
需要上下文
明确误报
扫描器看不到,人工补

不要写“看起来像”“应该没事”这种无法复查的备注。

第 4 步:把人工发现和自动结果放在一起

自动扫描没有发现的,不代表项目没有问题。

同一轮人工审查另外发现了:

  • P1:未认证请求可以轮换 X-API-Key 绕过限流;
  • P1:内部监听地址没有执行和公网监听一致的 TLS/loopback 检查;
  • P2:Read Key 可以导出全量 KV,并看到内部 Raft 地址;
  • P2:备份没有格式版本、校验和和恢复前完整性校验;
  • P2:Docker Compose 默认允许不安全模式和明文 HTTP;
  • P3:HTTP API 层缺少认证、限流和恢复接口测试。

这些问题扫描器没有直接报出来。

所以最后的交付不是:

扫描器报了 100 条

而是:

100 条自动结果完成分类;
其中没有一条可以直接交付为真实漏洞;
人工复核另有 2 个 P1、4 类中低风险。

我会留一张什么样的表

给团队或客户的结果表,不需要先贴 100 行原始 JSON。

先给这张表:

规则文件或模块输入来源是否可达结论下一步
ruby-rceGo/JS 字符串无否语言误报收敛规则
sql-concat函数名匹配无否文本误报增加反例测试
路径规则导入或路由无否上下文误报调整规则范围
限流绕过HTTP 入口外部请求是真实 P1限流键加入真实 IP
内部明文监听节点启动网络是真实 P1增加 loopback/TLS 强制检查

原始 JSON 仍然保留,用于复查和回归测试,不把原始结果当作最终报告。

三个不要

第一,不要因为一批误报就把整类规则关闭。先看规则是不是缺少语言隔离和反例测试。

第二,不要把 100 条结果全部塞进 baseline 后不再处理。baseline 应该放已经人工确认的历史问题,而不是代替确认。

第三,不要把 0 findings 当成安全结论。扫描器没有匹配到规则,只能说明当前规则和样本没有发现问题。

这段流程最省时间的地方

最省时间的不是立刻修代码,而是先把自动结果和人工结论分成两层。

自动结果负责提示位置,人工确认负责判断可达性和实际影响。只有确认后的结果,才适合进入修复排期、交付报告和回归测试。

扫描器给的是线索,报告给的是判断。

公开规则、示例报告和命令说明:

github.com/yuan1521913…

需要本地扫描器完整源码版:

code-audit-cli source package