1. 从一次“意外发现”说起Swagger API 信息泄露有多普遍几年前我在做一次常规的安全巡检目标是一个新上线的内部管理系统。按照惯例我会先用一些常见的路径去“敲门”看看有没有什么不该暴露的门窗。那天我随手在目标地址后面加了个/swagger-ui.html然后按下了回车。说实话我当时没抱太大希望因为开发团队的安全意识一直不错。但浏览器加载出来的页面让我心里“咯噔”一下——一个完整的、结构清晰的API文档界面赫然在目上面密密麻麻列出了系统所有的接口包括用户管理、订单处理、甚至是内部数据导出接口。更关键的是没有任何登录框没有任何认证阻拦我就这么直接进来了。我立刻尝试了其中一个标记为“获取所有用户列表”的接口点击“Try it out”然后执行。几秒钟后一个包含用户名、邮箱部分脱敏和注册时间的JSON列表就返回到了我的浏览器里。整个过程我没有输入任何账号密码没有绕过任何验证只是碰巧知道了一个“默认”的路径。那一刻我后背有点发凉。这可不是什么高深的0day漏洞这就是一个因为配置疏忽而敞开的“大门”。后来和开发同事沟通才知道他们在测试环境为了方便前后端联调开启了Swagger UI并且为了方便没有设置任何访问控制。上线时虽然切换了配置文件但某个依赖项依然将Swagger的端点暴露在了生产环境。这个故事不是个例。在我过去十年的安全评估经历里Swagger现在更多叫OpenAPI相关的信息泄露是我遇到最高频的Web应用安全问题之一。它太常见了常见到很多开发者和运维都忽略了它的危险性。Swagger本身是个极好的工具它能自动生成API文档让前后端协作变得无比顺畅。但问题就出在这个“自动”和“默认”上。很多框架在集成Swagger时默认配置就是为了方便开发访问控制要么很弱要么干脆没有。攻击者不需要懂复杂的业务逻辑不需要破解加密算法他们只需要像我们刚才做的那样用一份公开的、长达数百条的“默认路径列表”像敲门一样逐个尝试就有可能直接走进系统的“后花园”。这种漏洞的危害是立竿见影的。攻击者拿到API文档就等于拿到了系统的“地图”和“说明书”。他们能清楚地知道系统有哪些功能用户注册、登录、支付、数据查询、文件上传……每个功能怎么调用需要什么HTTP方法GET、POST、参数叫什么名字、参数是什么类型。接口的潜在弱点哪些接口可能没做权限校验比如那个“获取所有用户”的接口哪些接口的参数可能被篡改。有了这份地图后续的攻击就变成了按图索骥。他们可以系统地探测每个接口寻找未授权访问、越权操作、SQL注入、逻辑漏洞等等。原本需要大量猜测和模糊测试的工作现在变成了目标明确的“打靶”。所以千万别把Swagger信息泄露当成一个“低危”问题它往往是导致更大安全事件的“突破口”和“放大器”。2. 手动检测太费劲自动化工具来帮你“扫楼”知道了Swagger泄露的严重性那怎么发现它呢最原始的方法就是像我开头做的那样手动在浏览器里拼接那些默认路径。网上能找到很多这样的路径列表就像我们文章开头原始内容里列出的那一长串从/swagger-ui.html到/api/v3/api-docs可能有上百个。对于一个目标手动把这些路径试一遍已经够枯燥了。如果你面对的是成百上千个系统或子域名这工作量简直无法想象。所以我们必须借助自动化。这里我分享几个我实战中常用的思路和工具它们就像给你的扫描器装上了“Swagger专用雷达”。首先最基础但高效的使用带字典的目录扫描工具。工具不是重点思路才是。你可以用dirsearch、gobuster、ffuf或者任何你顺手的工具。核心在于那份“字典”也就是Swagger的常见路径列表。你需要自己整理一份全面的、随时更新的TXT文件。怎么整理呢除了收集公开列表我建议你结合历史渗透测试报告和内部资产的特点来补充。比如你们公司常用Spring Boot那就要重点包含/v2/api-docs、/v3/api-docs如果用了别的框架路径可能不同。把这份字典加载到扫描器里让它去批量“敲门”。# 使用 dirsearch 的示例命令 python3 dirsearch.py -u https://target.com -w ./swagger_paths.txt -e html,json -t 50 # 使用 ffuf 的示例命令 ffuf -u https://target.com/FUZZ -w swagger_paths.txt -mc 200,301,302 -fs 0跑完之后你会得到一堆状态码为200成功或者302重定向到登录页这也值得关注的路径。接下来就需要人工去快速判断了。一个典型的Swagger UI页面你一眼就能认出来。其次进阶玩法从源代码和网络空间测绘中寻找线索。手动扫描是针对已知目标。如果你想主动发现互联网上存在这个问题的资产那就需要更广的视角。网络空间测绘引擎比如FOFA、Shodan、ZoomEye。它们允许你使用特征语法进行搜索。例如在FOFA里你可以用bodyswagger-ui或者titleSwagger UI来查找。原始文章里提到的swagger/index.html也是一个很精准的语法。这些引擎能帮你快速定位一大批可能存在问题的系统。源代码监控在企业的安全左移实践中可以在代码仓库GitLab、GitHub设置安全扫描规则寻找项目配置文件如application.properties、application.yml中是否包含swagger.ui.enabledtrue且没有配置swagger.ui.basic-auth或security相关语句。在CI/CD流水线中拦截这类不安全的配置比上线后再修补要有效得多。最后识别与验证它到底是不是Swagger泄露扫出来一个/api-docs路径返回了一堆JSON怎么确认看内容一个标准的Swagger/OpenAPI规范JSON开头通常是这样的结构{ swagger: 2.0, info: { title: Sample API, description: API documentation for the system, version: 1.0.0 }, host: api.example.com, basePath: /v1, paths: { ... }, definitions: { ... } }或者 OpenAPI 3.0 的{ openapi: 3.0.0, info: { ... }, servers: [ ... ], paths: { ... }, components: { ... } }如果你看到paths里面包含了像/users、/orders/{id}这样的具体接口描述那基本就实锤了。更进一步你可以访问这个JSON文件同级目录下可能存在的swagger-ui.html如果能打开完整的UI界面那就是最直观的证据。3. 实战演练当工具遇到海量接口我们如何高效“攻击”假设我们已经成功发现了一个未授权的Swagger JSON端点http://vuln-target.com/api/v2/swagger.json。打开这个JSON文件里面定义了几十个甚至上百个API接口。手动去测试每一个接口的未授权访问、越权或者参数注入漏洞显然不现实。这时候我们就需要将自动化提升到下一个层级自动化解析Swagger文档并生成攻击载荷。原始文章里提到了一个叫swagger-hack的工具它的思路非常对路。这类工具的核心工作流程一般分三步解析读取你提供的Swagger JSON URL解析出所有的paths接口路径和每个路径支持的operationsGET, POST, PUT, DELETE等。生成根据每个接口的定义特别是parameters部分自动生成可以发送的HTTP请求。对于有示例值example的参数就用示例值没有的就用一些安全测试常用的测试值比如数字用1字符串用“test”ID用“1”等。探测将生成的请求批量发送出去然后分析响应。它的判断逻辑通常是如果接口返回了非40x如200、500状态码并且响应体中含有业务数据而非单纯的“未授权”错误信息则标记为“可能存在未授权访问”。我们来模拟一下这个过程。假设我们有一个简单的swagger-hack脚本这里用Pythonrequests和prance库模拟核心思想真实工具更复杂import requests import json import sys def swagger_auto_test(swagger_url): # 1. 获取并解析Swagger文档 try: resp requests.get(swagger_url, timeout10) swagger_doc resp.json() except Exception as e: print(f[-] 获取Swagger文档失败: {e}) return base_url swagger_doc.get(host, ).rstrip(/) if not base_url.startswith(http): # 从原始URL推断 from urllib.parse import urlparse parsed urlparse(swagger_url) base_url f{parsed.scheme}://{parsed.netloc} paths swagger_doc.get(paths, {}) print(f[*] 从文档中解析出 {len(paths)} 个路径) # 2. 遍历所有路径和方法 for path, methods in paths.items(): for method, details in methods.items(): if method.lower() not in [get, post, put, delete]: continue # 构造请求URL full_url base_url path print(f\n[*] 测试: {method.upper()} {full_url}) # 3. 简单生成测试参数实际工具会根据参数类型生成更丰富的测试用例 params {} json_data None param_list details.get(parameters, []) for param in param_list: if param.get(in) query: params[param[name]] test # 简单赋值 elif param.get(in) body: json_data {test: data} # 简单构造一个body # 4. 发送请求 try: if method.lower() get: resp requests.get(full_url, paramsparams, timeout5) elif method.lower() post: resp requests.post(full_url, jsonjson_data, timeout5) # ... 处理其他方法 # 5. 简单判断状态码为2xx且响应内容较长可能意味着成功访问 if 200 resp.status_code 300 and len(resp.content) 100: print(f[!] 潜在未授权访问: 状态码 {resp.status_code}, 长度 {len(resp.content)}) # 可以在这里将结果保存到文件 else: print(f[-] 访问被拒绝或失败: 状态码 {resp.status_code}) except requests.exceptions.RequestException as e: print(f[-] 请求失败: {e}) if __name__ __main__: if len(sys.argv) ! 2: print(用法: python swagger_scanner.py swagger_json_url) sys.exit(1) swagger_auto_test(sys.argv[1])但是这里有几个“坑”我必须提醒你误报与漏报工具可能把一些返回友好错误页面的接口状态码200但内容是“参数错误”误判为成功。也可能漏掉一些需要特定参数格式或头部如Content-Type: application/json才能触发的漏洞。所以工具的产出必须经过人工复核。风险控制这种自动化测试是向生产系统发送真实流量。如果你没有获得明确授权绝对不要这么做它可能会创建垃圾数据如POST创建测试订单、修改系统状态如PUT修改配置、甚至触发DDoS。在授权测试中也最好先在测试环境进行并使用更温和的测试参数。工具进化除了swagger-hack社区还有像Swagger-EZ、AutoSwagger等工具有些甚至集成到了像Burp Suite的插件中可以直接在代理流量里识别和测试Swagger端点更加方便。自动化工具极大地提升了效率但它不是“银弹”。它帮你完成了最繁琐的“跑腿”工作把可疑的接口筛选出来而真正的漏洞确认、利用链挖掘和危害评估依然需要安全工程师的经验和判断。4. 亡羊补牢针对Swagger泄露的立体防护方案发现了漏洞接下来最关键的一步就是修复。防护Swagger信息泄露绝不是简单地在生产环境关掉它就行虽然这是最直接的方法。我们需要根据不同的环境和需求采取分层的防护策略。下面这个表格对比了几种常见方案你可以根据实际情况选择防护策略具体操作优点缺点适用场景环境隔离生产环境彻底禁用Swagger自动暴露。最彻底根除风险。牺牲了生产环境的API文档可读性不利于线上问题排查和第三方对接。对内部管理系统、无对外API文档需求的系统。访问控制1.应用层集成Spring Security、Shiro等对Swagger UI路径进行认证和授权。2.网络层通过Nginx/Apache配置IP白名单只允许公司内网或运维IP访问。3.基础认证为Swagger UI页面添加简单的HTTP Basic Auth。灵活可以在提供便利的同时控制访问源。IP白名单和基础认证实现简单。应用层集成有一定开发成本IP白名单在移动办公场景下不便HTTP Basic Auth密码可能被弱口令或泄露。需要为内部开发、测试或合作伙伴提供API文档的系统。内容管控1.敏感信息脱敏在Swagger配置中使用注解过滤掉接口返回的敏感字段如密码、手机号的示例值。2.接口分组/隐藏利用ApiIgnore等注解将内部管理接口、高危接口从文档中隐藏。即使文档被未授权访问也能减少信息暴露。依赖开发人员规范使用注解可能有遗漏无法防止接口路径本身的暴露。所有暴露Swagger文档的环境作为一道补充防线。动态启用通过Profile或配置中心控制Swagger仅在某些环境如dev、test启用。符合开发流程安全与便利兼顾。需要确保上线前配置切换正确依赖严格的发布流程。拥有成熟CI/CD流程和配置管理能力的团队。让我重点展开一下“访问控制”里最实用的两种做法并给出可落地的代码片段。方案一Spring Boot Spring Security 集成认证这是Java生态里最经典的组合。你不需要关掉Swagger只需要确保访问它的路径时用户是登录过的。Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 定义Swagger相关资源的访问规则 .antMatchers(/swagger-ui.html, /swagger-resources/**, /v2/api-docs, /v3/api-docs, /webjars/**).authenticated() .anyRequest().permitAll() // 其他API请求按业务规则配置 .and() .formLogin() // 使用表单登录也可以改成httpBasic() .and() .csrf().disable(); // 根据实际情况决定是否禁用CSRF } Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 配置内存用户或数据库用户这里示例一个内存用户 auth.inMemoryAuthentication() .withUser(api-doc-viewer) .password(passwordEncoder().encode(YourStrongPasswordHere!)) // 务必使用强密码 .roles(DOC_VIEWER); } Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }这样配置后任何人尝试访问http://your-site/swagger-ui.html都会被重定向到登录页面。只有输入正确的账号密码才能看到API文档。方案二使用Nginx做IP白名单限制如果你不想动应用代码或者应用本身没有鉴权框架那么在网关层做限制是最快的。假设你的运维后台IP段是192.168.1.0/24。server { listen 80; server_name api.your-company.com; location ~ ^/(swagger-ui|api-docs|v2/api-docs|v3/api-docs|swagger-resources) { allow 192.168.1.0/24; # 允许内网IP段 deny all; # 拒绝其他所有IP # 将请求代理到后端应用 proxy_pass http://your-backend-app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 其他API请求的正常代理规则 location / { proxy_pass http://your-backend-app; } }这样一来从公网直接访问Swagger路径的请求会被Nginx直接拒绝返回403只有从公司内网发起的请求才能到达后端应用。这种方法简单粗暴且有效。注意无论采用哪种防护方案切记不要在代码或配置中留下真实的密码和密钥。像上面示例中的密码应该通过环境变量、配置中心或密钥管理服务在部署时注入。直接写在代码里提交到版本库相当于把钥匙挂在门上防护就失去了意义。5. 融入SDL让Swagger安全成为开发流程的一部分真正的安全不是一次性的漏洞修补而是融入软件开发生命周期SDL的习惯。对于Swagger这类开发阶段引入的风险我们尤其需要“左移”在代码编写和构建阶段就介入。第一关代码提交前——Git Hooks与IDE插件。可以在团队的Git仓库中配置pre-commit钩子使用脚本扫描即将提交的代码文件特别是配置文件检查是否有在生产环境开启Swagger且未配置安全控制的“危险模式”。例如一个简单的脚本可以检查application.yml文件是否同时包含swagger.ui.enabled: true和spring.security的相关配置缺失。虽然这不能完全杜绝但能在第一时间提醒开发者。第二关持续集成CI流水线——自动化安全扫描。这是最关键的一环。在你的Jenkins、GitLab CI或GitHub Actions流水线中加入一个安全扫描步骤。这个步骤可以使用SAST工具像Checkmarx、Fortify、SonarQube配合安全插件或开源的Semgrep、CodeQL。你可以编写自定义规则专门检测项目中对Swagger的“不安全配置模式”。例如检测Java代码中Docket对象的enable(true)调用且上下文中没有出现安全配置。使用配置检查工具对于配置文件可以使用像yaml-lint或自定义脚本进行模式匹配。比如在application-prod.yml中如果发现了swagger.ui.enabledtrue则直接让构建失败并给出明确的错误信息“生产环境配置文件禁止启用Swagger UI请检查配置”。第三关制品与部署——环境隔离与审计。确保不同环境开发、测试、预生产、生产使用独立的、正确的配置文件。通过配置中心如Spring Cloud Config、Apollo严格管理生产环境的配置确保Swagger在生产环境是默认关闭的。同时对所有环境的部署清单进行定期审计确保没有“错误”的配置被带到线上。第四关监控与响应——最后的防线。即使防护措施都做了也要有监控。可以在应用日志中对访问Swagger相关路径的请求进行高等级日志记录尤其是来自非白名单IP的访问。将这些日志接入SIEM安全信息与事件管理系统设置告警规则。一旦发现异常访问尝试安全团队可以立即收到通知并介入调查。我经历过一个真实的案例团队在代码中通过环境变量来控制Swagger的开关本意是好的。但在一次紧急上线时运维同学为了排查问题临时在线上容器环境变量里加上了SWAGGER_ENABLEDtrue事后却忘了删除。这个配置就这么在生产环境“静默”运行了两周直到被一次外部扫描发现。这件事告诉我们流程和工具能解决大部分问题但人的意识和最后的监控审计同样不可或缺。把Swagger的安全检查做成CI/CD流水线里一个必过的“卡点”就像编译和单元测试一样让它成为开发习惯的一部分才能真正地从源头降低风险。