3. ZAP核心架构与自动化接口
要把 ZAP 从“手工点按钮的代理工具”变成“可编排、可回归、可进 CI 的安全测试组件”,第一步不是写脚本,而是理解它的内部结构:请求从哪里进来、经过哪些引擎、结果存在哪里、外部程序又能通过哪些入口去驱动它。ZAP 的自动化能力,本质上来自它把“代理—扫描—扩展—接口”四层解耦,任何一层都可以被外部程序替换或调用。
一、代理层:所有流量的统一入口
ZAP 默认以中间人代理(MITM Proxy)方式工作,监听本地端口(默认 8080)。浏览器、移动端或自动化脚本把 HTTP/HTTPS 流量指向该端口后,ZAP 会为每个请求建立一条“会话记录”,并把它同时送入两条链路:一条是被动扫描链路,只观察流量、不主动发包;另一条是主动扫描链路,由 Spider、AJAX Spider 或主动扫描器主动构造请求。
HTTPS 场景下,ZAP 需要用自己的根证书(owasp_zap_root_ca.cer)签发站点证书,因此客户端必须信任该根证书,否则会出现证书错误。这一点在自动化中尤其关键:用 Python requests 或 Java HttpClient 走 ZAP 代理时,要么把 ZAP 根证书加入信任库,要么显式关闭校验(仅限测试环境)。
代理层还承担“作用域(Scope)”过滤。只有落在 Scope 内的 URL 才会被记录、扫描和报告,Scope 之外默认只做转发。自动化脚本通常先通过 API 设置 Scope,再启动扫描,避免把无关域名卷进结果。
二、扫描引擎:被动、主动与爬虫的分工
ZAP 的扫描能力由多个引擎组成,它们职责不同、触发方式也不同:
- 被动扫描器(Passive Scanner):不发送新请求,只分析已经流经代理的响应,检查缺失安全头、Cookie 属性、信息泄露、CSRF 令牌缺失等。它随流量自动运行,适合在功能测试或爬虫过程中“顺带”完成。
- Spider:传统 HTML 链接爬虫,解析
<a href>、表单等,适合服务端渲染页面。 - AJAX Spider:基于浏览器(如 Firefox/Chrome)驱动,能执行 JavaScript、点击按钮,适合 SPA 和前端路由站点。
- 主动扫描器(Active Scanner):对已发现的 URL 和参数主动注入 payload,检测 SQL 注入、XSS、路径穿越等。它开销大、可能影响业务,必须限定 Scope 和并发。
自动化中常见的顺序是:设置 Scope → 运行 Spider/AJAX Spider 发现 URL → 等待被动扫描完成 → 对目标 URL 启动主动扫描 → 轮询进度 → 拉取告警。这个顺序不是强制的,但符合“先发现、再观察、后攻击”的工程逻辑。
三、扩展机制:Add-on 与脚本
ZAP 的功能几乎都以 Add-on(扩展)形式存在,核心只保留代理、会话和 API 框架。Add-on 通过 Marketplace 安装,例如 ascanrules(主动扫描规则)、pscanrules(被动扫描规则)、ajaxspider、openapi、graphql 等。每个 Add-on 会向核心注册新的扫描规则、API 端点或 UI 面板。
比 Add-on 更轻量的是脚本(Scripting)。ZAP 支持用 JavaScript、Python(Jython)、Groovy 等编写脚本,挂载到不同“脚本类型”上,例如:
Standalone:手动或 API 触发的独立脚本;Active Rules:自定义主动扫描规则;Passive Rules:自定义被动检查;Proxy:在请求/响应经过代理时改写内容;Authentication:自定义认证流程。
这意味着团队可以把内部规范(如“所有接口必须带 X-Trace-Id”)写成被动规则脚本,随扫描自动执行,而不必改 ZAP 源码。
四、自动化入口:CLI、REST API 与客户端
ZAP 对外提供三类自动化入口,它们最终都落到同一套核心 API 上:
| 入口 | 典型用途 | 关键点 |
|---|---|---|
CLI(zap.sh / zap.bat) | 快速启动、无头模式、加载配置 | -daemon 无界面、-config 覆盖参数、-port 指定端口 |
| REST API | 任意语言驱动、CI 集成 | 默认 /JSON/、/XML/ 视图,需 API Key |
| Python / Java 客户端 | 编写可维护的测试代码 | 封装 API,提供 zap.core、zap.spider、zap.ascan 等命名空间 |
无头启动的典型命令如下(示例参数,非实测结果):
./zap.sh -daemon -host 127.0.0.1 -port 8090 \
-config api.key=change-me-123 \
-config api.addrs.addr.name=.* \
-config api.addrs.addr.regex=true
启动后,所有操作都可以通过 REST API 完成。例如用 curl 触发一次 Spider:
curl "http://127.0.0.1:8090/JSON/spider/action/scan/?apikey=change-me-123&url=http://target.local&recurse=true"
返回的 scan 字段是扫描 ID,随后用 /JSON/spider/view/status/?scanId=0 轮询进度,直到 status 为 100。主动扫描同理,使用 /JSON/ascan/action/scan/ 与 /JSON/ascan/view/status/。告警通过 /JSON/core/view/alerts/ 获取,可按 baseurl 过滤。
Python 客户端把上述调用封装为对象方法,代码更接近自然语言:
from zapv2 import ZAPv2
zap = ZAPv2(apikey='change-me-123',
proxies={'http': 'http://127.0.0.1:8090',
'https': 'http://127.0.0.1:8090'})
zap.spider.scan('http://target.local')
while int(zap.spider.status()) < 100:
time.sleep(2)
zap.ascan.scan('http://target.local')
for alert in zap.core.alerts(baseurl='http://target.local'):
print(alert['risk'], alert['name'], alert['url'])
Java 客户端(org.zaproxy.clientapi)结构类似,通过 ClientApi 构造 Spider、Ascan、Core 等子对象,适合嵌入 Maven/Gradle 构建。
api.addrs 白名单,而不是简单设为 .*。五、把四层串起来看
下图把代理层、扫描引擎、扩展机制和自动化入口放在同一张架构关系里,帮助理解“外部脚本的一次调用,最终在 ZAP 内部经过了什么”。
从图中可以看到:无论你用 CLI、REST API 还是 Python/Java 客户端,调用都会汇聚到 ZAP 核心;核心再驱动代理层和扫描引擎;而 Add-on 与脚本则向核心注册新的规则和 API,从而扩展扫描能力。理解这条链路后,编写自动化脚本时就能明确每一步“落在哪一层”,也更容易定位问题——例如扫描没有结果,可能是 Scope 没设对(代理层),也可能是主动扫描规则未启用(扩展层)。
-daemon 无头模式并固定端口,同时把 API Key 通过环境变量注入,避免硬编码在脚本或流水线文件中。