Swagger 接口文档暴露防护实战配置

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,并把批量探测脚本纳入每次发版的检查清单。加固完成后,务必用探测脚本在公网与内网各验证一次,确认文档端点按策略分流,而不是简单地”看起来访问不了了”。