无OpenAPI/Swagger规范时,OWASP ZAP的API认证、JWT复用及自动化扫描问题求助
我之前在纯API场景下用ZAP 2.9.0扫的时候,也踩过一模一样的坑——认证配置不对导致扫描没反应,JWT不会自动带,甚至连入口点都找不到。咱们一步步拆解解决:
你的核心问题是选错了认证方式,ZAP默认的表单认证是给带登录页面的Web应用设计的,纯API得用脚本式认证来主动调用登录接口拿Token:
精准配置Context范围
先确认Session > Contexts > Default Context里的Include in Context规则是准确的,比如https?://your-real-host/.*,一定要勾选激活这个Context(左侧Contexts面板里打勾),不然ZAP不会识别目标API。编写认证脚本获取JWT
打开ZAP顶部菜单Tools > Scripts,右键Scripts选New Script:- 类型选
Authentication Script,语言选ECMAScript - 粘贴下面的脚本(根据你的登录API格式调整请求体和Token提取逻辑):
function authenticate(helper, paramsValues, credentials) { // 构造登录请求 const loginReq = helper.prepareRequest(); loginReq.setMethod("POST"); loginReq.setHeader("Content-Type", "application/json"); loginReq.setURI("https://your-real-host/login"); // 替换成你的登录接口路径 // 用已添加的用户凭证构造请求体 const loginBody = JSON.stringify({ username: credentials.getParam("username"), password: credentials.getParam("password") }); loginReq.setRequestBody(loginBody); // 发送请求并提取JWT const loginRes = helper.sendAndReceive(loginReq); const resBody = JSON.parse(loginRes.getResponseBody().toString()); const jwtToken = resBody.token; // 假设返回的JSON里token字段是JWT // 把Token设为全局请求头,后续所有请求都会自动带上 helper.getHttpSender().addGlobalHeader("Authorization", `Bearer ${jwtToken}`); return true; // 返回true表示认证成功 } - 保存脚本,比如命名为
api-jwt-auth.js
- 类型选
关联脚本到Context认证
回到Session > Contexts > Default Context > Authentication:- 认证方式选择刚才创建的脚本
- 确保
Users里添加的用户名密码是正确的,右键该用户选Set as Forced User - 确认
Forced User Mode enabled是开启状态
测试认证有效性
在ZAP的History面板里手动发送一个登录请求,或者用Quick Start栏输入登录API URL点击Attack,检查请求头里是否自动带上了Authorization: Bearer xxx。
扫描没动静大概率是没有可扫描的入口点,纯API不像Web应用能自动爬取,得手动喂给ZAP路径:
导入你的JS测试用例生成入口点
把JS测试用例里的HTTP请求转换成HAR文件(比如用Chrome开发者工具录制测试流程,导出HAR),然后在ZAP里File > Import HAR,ZAP会自动解析出所有API路径、查询参数和请求格式,作为扫描的基础。手动补充入口点(可选)
如果没有HAR,就在左侧Sites面板里右键目标主机,Add to Context把已知的API路径(比如/api/users、/api/orders)添加进去。关于Active Scan的疑问解答
- 预期作用:主动模拟攻击者对每个路径发送恶意Payload,检测SQL注入、路径遍历、未授权访问等漏洞
- 不需要手动提供所有路径,但需要初始入口点让ZAP扩展(纯API爬取能力弱,所以导入HAR是最高效的方式)
- 查询参数会被ZAP自动识别,扫描时会针对参数做模糊测试
被动扫描是监听ZAP代理的所有请求并自动分析漏洞,适合复用你的JS测试用例:
- 设置你的测试环境HTTP代理为
localhost:8080(ZAP默认代理端口) - 运行JS测试用例,所有请求都会经过ZAP并被记录到
History,同时自动完成被动扫描,生成告警 - 导入HAR文件后,ZAP也会对导入的请求自动做被动扫描
ZAP支持把UI配置导出成命令行参数,或者用脚本自动化:
导出Context配置
在ZAP里File > Export Context,保存为api-scan.context文件命令行扫描示例(snap版本)
zaproxy -cmd \ -importhar "/home/your-user/Documents/api-test-cases.har" \ -contextfile "/home/your-user/Documents/api-scan.context" \ -script "/home/your-user/Documents/api-jwt-auth.js" \ -forceduser \ -activeScan \ -report "/home/your-user/Documents/scan-report.html"注意:snap安装的ZAP对文件路径权限有限制,尽量把文件放在
~/Documents这类允许访问的目录Python脚本自动化(可选)
用ZAP的Python API编写更灵活的扫描脚本:import time from zapv2 import ZAPv2 # 连接本地ZAP实例 zap = ZAPv2(proxies={'http': 'http://localhost:8080', 'https': 'http://localhost:8080'}) # 加载Context和设置强制用户 zap.context.load_context('/home/your-user/Documents/api-scan.context') zap.users.set_forced_user('Default Context', 'your-username') zap.forcedUser.set_forced_user_mode_enabled(True) # 导入HAR测试用例 zap.import_har('/home/your-user/Documents/api-test-cases.har') # 启动主动扫描 scan_id = zap.ascan.scan('https://your-real-host/') # 等待扫描完成 while int(zap.ascan.status(scan_id)) < 100: print(f"扫描进度: {zap.ascan.status(scan_id)}%") time.sleep(5) # 生成HTML报告 with open('/home/your-user/Documents/scan-report.html', 'w') as f: f.write(zap.core.htmlreport())
- 如果JWT有过期时间,可以在认证脚本里添加Token过期检测逻辑,过期自动重新获取
- 扫描前关闭其他代理工具,避免端口冲突
- 若出现文件权限错误,检查snap的权限设置,或者把文件放在
~/Documents目录下
内容的提问来源于stack exchange,提问作者TPPZ




