测试与安全测试融合:DAST / SAST / IAST 三选

系列专栏:测试工程师每日一博 · Day 24(2026-08-06,周四)

上一篇:Day 23 · 微服务测试矩阵:Honeycomb 测试模型 + 单元/契约/E2E 决策树

这一篇是「测试 + 架构咬合」三联篇之后的延伸 ------ Day 21/22/23 解决了功能测试跟架构的咬合,Day 24 把安全测试这一根常被测试工程师忽略的柱子 接进来,作为「测试 + 非功能性需求」对子的第一篇。

目标:给一份 SAST / DAST / IAST 三档贯通的落地指南 + CI 三层时间门控,读完能让安全测试跟功能测试在同一套 CI 里同跑,不抢镜、不漏检、不重复造轮子。

一、为什么测试工程师必须懂安全测试

工业里一个危险的分工:测试工程师管"功能对不对",安全团队管"安不安全" 。听起来分工清晰,实际会让安全漏洞从评审 → 编码 → CI → 预发一路漏到生产,等到安全团队扫出时已经累积成"债"

具体撕掉两个常见误区:

误区 真相
"安全是安全团队的事" 90% 的安全漏洞发生在功能代码里(SQL 注入、XSS、PII 泄露),测试工程师写测试时眼睛一瞥就能拦下
"安全测试要懂渗透才行" DAST / SAST / IAST 三类都是工具自动扫,测试工程师的活是"接到 CI 流水线 + 看懂报告 + 红线拦截"

回扣 Day 17 §2.6 安全维度 + Day 19 协作地盘------测试工程师已经在 PR review 里看过 SQL 注入了 。Day 24 是把那条红线进化为系统化的工具链

二、DAST / SAST / IAST 三种本质区分

这三种安全测试常见被混着用。先用一张表对清楚:

维度 SAST DAST IAST
全称 Static Application Security Testing Dynamic Application Security Testing Interactive AST
中文名 静态安全测试 动态安全测试 交互式(运行时)
输入 源代码 / bytecode 运行中的 HTTP endpoint 跑测试时的运行时插桩
跑在哪一阶段 编码期 / PR 集成期 / 预发 集成测试期
能发现 代码模式层漏洞(SQL 注入源码) 行为层漏洞(实际能 payload 攻击) 代码 + 行为 + 数据流
误报率 高(20%-40%) 中(5%-10%) 低(<5%)
速度 快(秒级) 慢(分钟到小时级) 跟功能测试同速
代表工具 SonarQube / Semgrep / CodeQL OWASP ZAP / Burp Suite / Nuclei Contrast Security / Seeker

读完这张表的关键结论 :三种不是替代关系,是互补关系。它们在不同阶段守不同红线。

2.1 三种测试的速度-有效性曲线

复制代码
有效性 │                DAST ●
       │              ●
       │            ●        ← 找得到真漏洞,但慢
       │          ●
       │       ● IAST ★
       │     ●               ← 平衡点(本篇推荐)
       │   ●
       │ SAST ●              ← 快,但误报多
       │●─────────────────────── 速度
         秒级     分钟级    小时级

IAST 在曲线中段------速度跟集成测试同速(因为它就是在集成测试里插桩),有效性接近 DAST。这就是为什么工业里 IAST 是"性价比之王"------但门槛也最高(要装 agent,要业务配合)。

2.2 三种测试本质对应三种「认知层」

换个角度看 --- DAST / SAST / IAST 各自看到的安全世界分别对应认知层级:

  • SAST 看代码 :就像静态类型检查(TypeScript / Java 类型系统),
    能拦住「明显会出错」的写法,但拦不住「运行时拼出来的恶意输入」;
  • DAST 看行为 :就像功能测试,从外部接口真实发起攻击,
    能发现「代码看着没毛病但行为上能被攻破」的漏洞;
  • IAST 看数据流 :就像 debug 时的断点,
    它盯着「恶意 payload 从 HTTP 进入到 JDBC 出去的整个数据路径」,
    看得到 SAST 看不到的「拼接发生在运行时」、
    也能修掉 DAST 发现晚的「要发请求才知道」。

这三种认知层的差异,意味着任何一种单独都不够

SAST 看不到运行时拼接、DAST 看不到代码上下文、

IAST 看不到「跑测之外的代码路径」。

工业实践里只有「三选一」的团队,

都会出现「同一类型漏洞反复发生」的反复事故。

这一观察也对应到 Day 1 测试金字塔的精神:

金字塔不是「单测就够了」,是「每一层都不可少」 ,

安全测试同理。

三、SAST:静态扫描 + PR 红线

最便宜的一档,所有团队都该接

3.1 Semgrep 案例:SQL 注入红线

yaml 复制代码
# .semgrep.yml
rules:
  - id: java-sql-injection-string-concat
    patterns:
      - pattern: |
          String $SQL = "...$VAR...";
          $STMT.executeQuery($SQL);
      - pattern-not: |
          $STMT = $CONN.prepareStatement(...);
    message: "可能 SQL 注入 --- 字符串拼接构造 SQL,建议改 PreparedStatement"
    languages: [java]
    severity: ERROR
    metadata:
      cwe: "CWE-89: SQL Injection"

  - id: pii-logging-mobile
    pattern-regex: 'log\w*\([^)]*\b(user|mobile).*(get)?Mobile\([^)]*\)'
    message: "可能 PII 泄露 --- 日志直接打了手机号"
    severity: WARNING
    metadata:
      cwe: "CWE-532: Insertion of Sensitive Info into Log"

接到 PR check:

yaml 复制代码
# .github/workflows/sast.yml
- name: Semgrep SAST
  uses: returntocorp/semgrep-action@v1
  with:
    config: .semgrep.yml
  env:
    SEMGREP_RULES: .semgrep.yml

关键设计:

  • SAST 只接 PR,不接每夜------快速反馈,5 秒级;
  • 误报一定要 tune(用 pattern-not 排除合理用法),否则开发会装作没看见;
  • severity ERROR 必须红灯拦 PR,WARNING 进 review 评论。

3.2 SonarQube vs Semgrep

我推荐主力用 Semgrep + SonarQube 补 Code Quality:

工具 强项 适合场景
Semgrep 规则可写、CI 友好 自定义漏洞红线(业务规则 + 安全)
SonarQube 长期质量度量、复杂度 综合 Code Health,但不擅长安全定制
CodeQL(GitHub) 跨过程分析能力强,但慢 每夜深度扫,不进 PR

回扣 Day 7 --- 时间门控同样适用于安全测试:SAST 进 PR,DAST 进每夜

3.3 SAST 误报 tune 的工程动作(实战)

接好 SAST 不等于「上线就高枕无忧」------刚开始接的 1-2 个月,

误报可能占 30-50%,如果不去 tune,

SAST 红灯会变成「开发都装看不见」的噪声源。

三个标准 tune 动作:

动作一:跑一周后,把所有 ERROR 级别告警分类:

python 复制代码
# tools/sast-triage.py
import json
from collections import Counter

def triage_sast_rules(report_path):
    report = json.load(open(report_path))
    rule_freq = Counter(f['rule_id'] for f in report['findings'])
    return {
        'frequent_rules': rule_freq.most_common(10),
        'recommendation': 'top 3 规则误报 > 50% 必 tune, 0 命中规则该 review',
    }

动作二:误报的根因有 4 种,分别用不同方式 tune:

误报根因 表现 tune 方式
业务库 API 被误判 safeMapper.query() 被报注入 pattern-not 排除业务库名
已用 PreparedStatement 但识别错 看到 + 拼接就报 用 metavariable-pattern 精确匹配
测试 fixture 被扫出 password' OR 1=1 被报 .semgrep.yml 排除 src/test/**
框架内置 SQL builder 被扫 MyBatis Stream API 误报 配置预设 library 排除

动作三 :每月一次「假阳复盘」。top 5 假阳规则进 issue tracker,

分配 owner 限 30 天 tune ------ 不能让假阳永远挂在那儿。

回扣 Day 20 §10 指标反模式 ------ SAST 工具一旦不 tune,
1 年后会成为「摆设」
,红灯被开发屏蔽,工具还在跑但保护力归零。

这是工程上常见的「安全债」坑:工具上线了,后续投入没跟上,

渐渐就流于形式。

四、DAST:动态扫描 + 周期性深度

DAST 跟 SAST 互补 --- 它不知道代码长什么样,只用 payload 攻击运行中的服务

4.1 OWASP ZAP 接到 CI

python 复制代码
# tools/dast-scan.py
import subprocess, json, sys

def zap_scan(target_url: str, intensity: str = "normal"):
    """
    对 target_url 跑 OWASP ZAP 扫描;
    intensity 分档:quick(2min)/ normal(15min)/ deep(60min)
    """
    result = subprocess.run([
        "zap-cli", "quick-scan",
        "--self-contained",
        "-t", target_url,
        "-i", intensity,
        "-f", "json",
        "-o", "zap-report.json",
    ], capture_output=True)
    
    report = json.load(open("zap-report.json"))
    high_risk = [alert for alert in report["alerts"]
                 if alert["riskcode"] in ("3", "2")]   # High + Medium
    if high_risk:
        print(f"❌ DAST found {len(high_risk)} high/medium-risk issues")
        for a in high_risk:
            print(f"  - [{a['riskcode']}] {a['name']}: {a['url']}")
        sys.exit(1)
    print(f"✅ DAST clean ({len(report['alerts'])} low/info)")

zap_scan(target_url="https://staging.internal", intensity="normal")

接进每夜:

yaml 复制代码
# .github/workflows/dast-nightly.yml
name: Nightly DAST Scan
on:
  schedule: [{cron: "0 3 * * *"}]   # 每夜 3 点
  workflow_dispatch:
jobs:
  dast:
    runs-on: security-runner
    steps:
      - name: Run OWASP ZAP
        run: python tools/dast-scan.py --intensity deep

DAST 不进 PR,只进每夜 + 发版前------慢、要起完整 staging。

4.2 DAST 的两个坑

坑一 :扫描需要认证态。如果 staging 有登录,DAST 扫不到登录后的接口,主要功能区域漏掉。解法:在 ZAP 配置里加 session token(预先登录拿 cookie):

python 复制代码
zap_cli_script = """
var sess = session.open("oauth");
sess.set("Cookie", "SESSION=...");
session.includeInContext("all", "https://staging.internal/.*");
"""
# 跑前先 load session

坑二 :写操作 DAST 会乱搞(POST /orders 真的会创建假单)。解法 :DAST 只对 staging 跑,且 staging 必须有"测试数据隔离"(回扣 Day 14 §6 数据治理红线)。

4.3 DAST 报告要进度量(Day 20 接入)

DAST 跑出来的告警不能只丢 issue tracker,要进 OKR 度量体系(回扣 Day 20 §6 测试资产):

yaml 复制代码
# dashboards/security-quarterly.q
panels:
  - title: 'DAST findings by severity(过去 90 天)'
    query: |
      SELECT
        severity,
        COUNT(DISTINCT finding_id) AS findings,
        AVG(days_to_fix) AS avg_sla_days
      FROM dast_findings
      WHERE detected_at > now() - interval '90 days'
      GROUP BY severity

三个对应度量:

  • DAST_findings_high_unfixed(P0 漏洞未修数);
  • DAST_mean_time_to_fix(漏洞平均修复时长);
  • DAST_recurring_rate (同类漏洞复发率 ------ 这条最有信号,
    高 = 团队没学到根因)。

对比 Day 20 §3 的测试质量指标体系,

安全维度的「recurrence rate」等价于「escape rate」------

都是漏检指标的镜像

五、IAST:运行时插桩 --- 性价比之王

IAST 的精髓:在跑功能测试时,顺手做安全检查,不用额外扫一次。

5.1 原理图

复制代码
┌────────────────────────────────────────────┐
│  功能测试跑(OrderCreateTest 等)              │
│     ↓ 调用                                  │
│  被测应用(已装 IAST agent)                   │
│     ├→ agent 拦 JDBC: 看到 "; DROP TABLE"  │
│     │  → 标记 "可能 SQL 注入"                │
│     ├→ agent 拦 HTTP out: 看到 SSN 字段     │
│     │  → 标记 "可能 PII 外发"                │
│     └→ 上报到 IAST 控制台                    │
└────────────────────────────────────────────┘

也就是说,功能测试覆盖到的代码路径,IAST 都会自动扫 ,且误报率 < 5%(因为它看到的是真数据流,不是静态推断)。

5.2 一个伪部署

yaml 复制代码
# docker-compose.iast.yml
services:
  app:
    image: order-service:iast
    environment:
      - JAVA_TOOL_OPTIONS=-javaagent:/agents/contrast-agent.jar
      - CONTRAST__API__URL=https://app.contrastsecurity.com
      - CONTRAST__API__SERVICE_KEY=${IAST_KEY}
      - CONTRAST__SERVER__NAME=staging-iast
  test-runner:
    image: test-runner:latest
    command: mvn test -Dspring.profiles.active=iast

注意整个集成测试套件就是 IAST 的"扫描面" ------测试覆盖越广,IAST 越有效(回扣 Day 20 §6 测试资产指标)。这是 Day 24 跟 Day 20 的关键咬合点。

5.3 IAST 的两个坑

坑一 :性能开销。IAST agent 让单测速度降到 60-70%。解法 :只对集成测试插桩,不对单测(单测本就跑得快,加 IAST 浪费);Spring profile 隔离。

坑二 :agent 装错版本。Java 升级到 21,旧 contrast-agent 没跟,跑出来零报。解法:agent 版本号进 OKR 度量(回扣 Day 20 §10 "指标反模式"------监控工具有效性)。

5.4 IAST 跟契约测试(Day 9)的天然咬合

IAST 的覆盖面跟功能测试同源 ------

功能测覆盖到的 endpoint,IAST 就会扫 ;

功能测覆盖不到的,IAST 也看不到。

这跟 Day 9 Pact 契约测试的边界完全一致:

契约保证跨服务协议,IAST 检查同一协议的安全

落地动作:把 IAST 控制台的报告按 Pact 契约对齐:

python 复制代码
# tools/cross_check_pact_iast.py
def cross_check(pacts, iast_findings):
    # 每个 Pact 对应一条 IAST 报告
    paired = []
    for pact in pacts:
        matching = [f for f in iast_findings if f.endpoint == pact.endpoint]
        paired.append({
            'pact_verified': pact.verified,
            'pact_endpoint': pact.endpoint,
            'iast_findings': len(matching),
            'iast_high': sum(1 for m in matching if m.severity == 'high'),
            'cross_coverage': bool(pact.verified and matching),
        })
    return paired

两份报告交叉看 会发现一类真相:

Pact 通过但 IAST 报危险 → 协议对但代码层有漏洞;

Pact 红但 IAST 没报 → 协议变了但新代码也没扫到(白测)。

这是 Day 24 在「测试 + 安全」融合块的最有价值产出:

测试工程师借 IAST 的报告回看契约测试,反向提质量

六、把三种接进 CI 全流程

把 Day 24 全部汇总到一张实施图:

复制代码
   提交 PR
     ↓
   ┌────────────────────────────┐
   │ PR Check(≤ 5 min)         │
   │   - SAST (Semgrep)         │ ← 静态扫描
   │   - 单测                    │
   │   - 契约 verify            │
   └────────────────────────────┘
     ↓ merge
   ┌────────────────────────────┐
   │ 集成测试(每夜,IT runner)  │
   │   - 集成测试 + IAST agent   │ ← IAST 插桩,功能+安全同跑
   └────────────────────────────┘
     ↓
   ┌────────────────────────────┐
   │ 每夜 + 发版前                │
   │   - DAST (ZAP) 对 staging   │ ← 行为层深度扫
   └────────────────────────────┘
     ↓
   生产

对应 Day 7 CI 时间门控 + Day 23 Honeycomb 决策树:

阶段 安全测试 速度 同跑的功能测试
PR SAST 秒级 单测 + 契约
集成 IAST 与功能测同速 Honeycomb 集成层
发版前 DAST 慢(但可以接受的 30 min) E2E 冒烟

每一档都"跟功能测试同跑",不抢镜

七、回扣系列

Day 在 Day 24 它接到的线
Day 1 测试金字塔 §6 SAST 跟单测同档
Day 4 Testcontainers §5 IAST agent 包装在容器里
Day 5 WireMock §4 DAST 配合 WireMock 隔离外部依赖
Day 7 CI 分层 §6 SAST 进 PR,DAST 进每夜
Day 9 契约测试 契约层天然有 IAST 插桩
Day 10 flaky 治理 DAST 周期性 brief 拿不到认证会 flaky
Day 14 数据治理 DAST 写操作必隔离
Day 17 PR review §3 SAST 红线 = PR review 自动化
Day 19 三方协作 DevOps 提供 staging,SRE 监控 IAST,测试主导
Day 20 OKR 度量 §5.3 agent 版本进度量(避免"工具失活")
Day 22 UI 自愈 DAST 跨 UI 流也能跑
Day 23 Honeycomb §6 SAST/IAST/DAST 全部对应金字塔四层

12 条对应。Day 24 是「测试 + 安全」第一次正面对接,跟 Day 21/22/23 三联篇形成完整「测试 + 非功能」补全。

八、反模式清单

反模式 后果 干掉的办法
SAST 误报多但没人 tune 开发忽视,红灯被屏蔽 §3.1 每月 review 假阳,加 pattern-not
DAST 在 PR 上跑 CI 时间从 5 分钟炸到 1 小时 §6 DAST 只在每夜
IAST agent 装到生产 性能砸了,生产出事故 §5 IAST 只装 staging
漏洞进了 ADR(漏洞登记册)但不修 安全债越积越深 漏洞必须有 SLA(高 7d / 中 30d / 低 90d)
只用 SAST 误以为是"安全了" 行为层漏洞(SAST 看不到)漏掉 三种都接,各有边界
DAST 扫 staging 时没数据 多数 endpoint 不暴露,扫不出 staging 装 synthetic 数据(回扣 Day 14)
IAST 接了但单测用 报告空白 + 性能砸 §5.3 用 Spring profile 隔离到集成测试

九、原则三句话

  1. SAST / DAST / IAST 互补,不可单选 ------ 三档各守一条线,缺一档都会暴露对应类型漏洞;
  2. IAST 是性价比之王(有效性高、误报少),但门槛高,适合中大型团队;
  3. 跟功能测试同行 ------ SAST 在 PR,IAST 在集成测试,DAST 在每夜,不抢功能测试的镜

9.1 一句工程口诀(可贴墙上)

读过本篇后,团队 review PR 时可以套用:

「PR 静扫、跑测插桩、夜放攻击」 ------

SAST 进 PR 守代码层,IAST 跟功能测插桩看数据流,

DAST 在每夜对 staging 发起真攻击。

三层各管一段,谁都替代不了谁

十、思考题

留给今晚:

  1. 你团队接了哪几档(SAST / DAST / IAST 全 / 部分)?最常见的盲点是哪一档?
  2. 上次安全漏洞在 PR / 集成 / 发版 / 生产哪一层发现的?哪一档该提前拦截?
  3. IAST 误报 < 5%,你的功能测试覆盖率如果只有 50%,IAST 的有效性会受多大影响?(慢思考)
  4. 你的 CI 时间里,有多少 % 是安全测试?如果 > 30%,该考虑 §6 的分层是否合理。

十一、TL;DR

  • 三种测试本质区分:SAST 看代码,DAST 攻行为,IAST 看数据流;
  • 各自落地档:SAST 进 PR(秒级)、IAST 进集成测试(同速)、DAST 进每夜(分钟到小时);
  • IAST 是性价比之王 ,但 agent 维护成本高,适合中大型团队;
  • 三种都必须接 ,不接是漏,全接是负担,分级是工程取舍;
  • 12 条对位系列前文,Day 24 完成「测试 + 安全」第一次正面咬合。

下一篇:Day 25 · 性能压测的容量规划:把"找拐点"上升为"找容量曲线方程"

Day 15 给过"拐点 × 0.7 当生产安全水位"的速写,Day 25 把它推到工程化 ------ 在多个并发档跑出来的散点上

拟合容量曲线方程、给容量数据库建模、用方程外推预测"加 5 个 pod 能扛多少 QPS"。下篇见 👋

相关推荐
达柳斯·绍达华·宁1 小时前
重读CV系列——&&4、Mask-RCNN学习
学习
fīɡЙtīиɡ ℡2 小时前
深入学习JAVA并发编程(上)
java·开发语言·学习
PC2005-cloud2 小时前
KubeSphere学习笔记:部署nginx
笔记·学习·nginx
Bernice橘子2 小时前
职场英语学习计划Day033
学习
依然鸣3 小时前
PTA团体程序设计天梯赛L2真题讲解L2-045-048
数据结构·c++·经验分享·学习·算法·pat考试·pat
星火跨境XINGHUOS4 小时前
独立站搭建后去哪里学运营?全链路学习路径拆解
学习
统计学小王子4 小时前
《数学少年-从正负号到几何原本》(第三章:“进退加减,循规而行“)--3.3 奶茶店流水账
学习·初中数学科普·数学科普
别催小唐敲代码4 小时前
STM32 定时器学习笔记:TIM 架构、输入捕获与 PWM 一篇讲透
笔记·stm32·学习
0566465 小时前
RAG 向量检索:从“查字“到“查意“
数据库·人工智能·学习·oracle