Swagger 接口文档暴露是生产环境最常见的敏感信息泄露来源之一。大量团队为了方便联调,把 Swagger UI 与 OpenAPI 规范文件随应用一起发布到线上,结果 /swagger-ui.html、/v3/api-docs、/doc.html 等路径对公网完全开放,攻击者只需一次 GET 就能拿到全部接口路径、参数结构、鉴权方式甚至内部管理接口。本文给出从框架层到网关层的接口文档暴露防护方案,配置可直接复制使用。
适用场景
- Spring Boot + springdoc-openapi 或 springfox 对外提供 REST 接口,文档路径与业务同域发布;
- Knife4j、YApi、Apifox 等工具生成的静态文档被一并打包进生产镜像;
- Nginx 或云 WAF 前置站点,需要在不改动业务代码的前提下屏蔽文档路径;
- 需要保留内网与固定办公出口访问文档的能力,公网侧必须收敛。
前置条件
- 应用可控编译与启动参数,能修改 Spring 配置或注入环境变量;
- Nginx 具备 server 段配置权限,站点使用独立 conf 便于
include; - 已梳理接口清单,明确哪些路径属于内部调试用途;
- 保留一份旧版本响应作为对照,便于验证改动确实生效。
原理说明
Swagger 生态由两部分组成。一是 OpenAPI 规范文件,默认路径为 /v3/api-docs 或 /v2/api-docs,内容是纯 JSON,描述全部接口;二是 渲染 UI 的静态资源,springdoc 为 /swagger-ui/index.html,Knife4j 为 /doc.html 并依赖 /webjars 目录。
防护必须分三层。第一层是框架层按环境关闭文档生成,从源头不产生规范文件;第二层是网关层按路径拦截,覆盖框架开关失效、多应用共存的情况;第三层是响应层把拒绝响应统一改写为 404,消除”路径存在”这一信息。只做框架开关容易被旧包回滚打破,只做 Nginx 拦截则探测者仍可从错误码差异判断路径存在性。
需要注意,仅在管理后台隐藏 UI 入口不构成防护。前端菜单不显示,直接访问规范文件路径依然可以获取完整接口定义,这类”伪加固”在渗透测试中最常被首先识破。
另一个常见误区是只处理 UI 路径而漏掉规范文件。只封 /swagger-ui.html 而放过 /v3/api-docs,等价于没有防护——规范文件本身包含全部接口定义,用任意 OpenAPI 客户端导入即可还原出完整文档。同一应用还常常存在多个版本的规范路径(如 /api-docs、/swagger.json、/spec),因此拦截规则必须用正则一次覆盖全部已知路径形态,而不是逐个添加 location。
操作步骤
1. 框架层:按环境开关文档
# application-prod.yml
springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false
Knife4j 需额外关闭增强 UI 与生产保护:
knife4j:
enable: false
production: true
basic:
enable: true
若使用 springfox,改为 springfox.documentation.enabled=false。容器化场景可以用环境变量注入,无需重新打包镜像:
docker run -e SPRINGDOC_API_DOCS_ENABLED=false \
-e SPRINGDOC_SWAGGER_UI_ENABLED=false \
-d myapp:latest
2. 网关层:Nginx 精确拦截文档路径
location ~* ^/(swagger-ui|swagger-resources|v2/api-docs|v3/api-docs|doc\.html|webjars) {
allow 10.0.0.0/8;
allow 192.168.0.0/16;
allow 203.0.113.10; # 运维固定出口 IP
deny all;
proxy_pass http://127.0.0.1:8080;
error_page 403 =404 /_notfound;
}
location = /_notfound { internal; return 404; }
这段配置的语义是:白名单来源正常反向代理到后端,其余来源被 deny all 拒绝并触发 403,再由 error_page 403 =404 把 403 改写为 404。注意不要在 deny all 之后直接写 return 404,否则白名单 IP 也会被一并拦截,文档在内网同样不可用。
3. 补充拦截其他规范与调试路径
location ~* ^/(openapi\.json|openapi\.yaml|api-docs|spec|api/schema|api/swagger) {
deny all;
error_page 403 =404 /_notfound;
}
如果站点同时部署了 Actuator、Druid 监控等调试端点,建议把路径规则合并到同一份 block-debug.conf,用 include 引入,避免每加一个应用就复制一遍正则。
4. 白名单集中管理
# /etc/nginx/conf.d/doc-allow.conf
allow 10.0.0.0/8;
allow 192.168.0.0/16;
allow 203.0.113.10;
allow 203.0.113.0/28;
主配置改为 include /etc/nginx/conf.d/doc-allow.conf;,后续调整白名单只改一个文件,降低遗漏风险。
5. 云 WAF 与源站兜底
若站点前置云 WAF,把同样的路径正则配置到 WAF 的自定义规则中,动作选择”拦截并返回 404″,而不是”返回 403″或”跳转验证页”。需要注意三点:部分云 WAF 默认放行 /.well-known/ 目录,若文档路径恰好落在该目录下需单独加规则;CDN 可能已缓存旧版本响应,规则上线后要手动刷新文档路径的缓存,否则公网仍能拿到 200 的旧内容;回源 IP 白名单要同步核对,避免出现”WAF 拦住了、直连源站仍可访问”的缺口。
# 源站兜底:仅允许 CDN 回源段访问,其余一律拒绝
location / {
allow 203.0.113.0/24; # CDN 回源地址段
deny all;
proxy_pass http://127.0.0.1:8080;
}
完成后再用源站 IP 直接带 Host 头访问一次,确认无法绕过边缘防护拿到文档,这一步是很多加固工作真正的漏洞所在。
配置验证
# 1. 语法检查并平滑重载
nginx -t && nginx -s reload
# 2. 公网访问应为 404,且不是 403、200
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/v3/api-docs
# 3. 白名单来源应正常返回 200
curl -s -o /dev/null -w "%{http_code}\n" --resolve example.com:443:127.0.0.1 \
https://example.com/v3/api-docs
# 4. 批量探测确认无残留端点
for p in /v3/api-docs /v2/api-docs /swagger-ui.html \
/swagger-ui/index.html /doc.html /api-docs /openapi.json; do
printf "%-40s %s\n" "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://example.com$p)"
done
第 4 步应在公网与内网各跑一次:公网全部 404,内网按白名单策略出现 200 与 404,说明分流生效。
常见问题
FAQ 1:框架层已经关闭了文档,还需要在 Nginx 拦截吗?
需要。框架开关依赖代码与镜像正确构建,历史案例中多次出现”开发环境改了配置、生产用旧包回滚”导致文档重新暴露;同机多应用部署时,Nginx 是唯一能统一覆盖全部后端的兜底。两层同时配置,任一层失效都不至于直接泄露。
FAQ 2:为什么建议返回 404 而不是 403?
403 明确告知探测者”路径存在但你没有权限”,攻击者会转而尝试伪造 X-Forwarded-For、寻找内网跳板或绕过 WAF 规则。404 让文档路径与任意不存在的路径表现一致,显著降低被针对性绕过的概率。前提是站点对 404 页面不做特殊渲染差异,也不要在响应头里留下后端特征。
FAQ 3:内网也需要访问文档,白名单如何维护?
优先用网段而非单个 IP,团队使用 VPN 或固定出口时把对应网段加入即可。不要为了省事放开整个办公网段,那等于暴露面没有收敛。所有白名单集中到一个 include 文件,纳入配置变更审计。
FAQ 4:用了云 WAF,还需要本地 Nginx 规则吗?
建议双保险。云 WAF 在公网边缘拦截,能挡住绝大多数扫描;但同一 VPC 内的横向访问、内网渗透、以及绕过 CDN 直连源站的请求都不经过 WAF。源站 Nginx 规则是最后一道防线,尤其在有回源 IP 白名单之外的其他入口时更必要。
总结
接口文档暴露的危害不体现在单次请求上,而在于把后续攻击成本降到极低。正确做法是框架层关闭生成、网关层拦截路径、响应层统一返回 404,并把批量探测脚本纳入每次发版的检查清单。加固完成后,务必用探测脚本在公网与内网各验证一次,确认文档端点按策略分流,而不是简单地”看起来访问不了了”。