Spring Boot Swagger未授权访问漏洞:从检测到修复的完整指南
2026/9/20 18:32:55 网站建设 项目流程

1. 从一个真实案例说起:Swagger 页面怎么就变成“后门”了

去年帮一个朋友做安全巡检,他用 Spring Boot 写了个内部管理系统,部署在公网一台云主机上,自认为“接口都加了 JWT 鉴权,稳得很”。我随手访问了一下/swagger-ui/index.html,页面直接弹出来了,所有 Controller 的接口路径、请求参数、返回结构、甚至部分示例数据一览无余。更麻烦的是,有几个接口的鉴权拦截器配置漏了路径匹配,等于说攻击者拿着 Swagger 页面当“菜单”,挨个试就能找到未授权的写接口。

这不是个例。Spring Boot 生态里,Swagger(现在主流是 springdoc-openapi 或 springfox)几乎是标配的接口文档工具,开发阶段确实好用,但很多人打包上线时忘了关,或者关了 UI 却没关/v3/api-docs这类元数据端点。结果就是:接口文档变成了攻击者的侦察地图

这篇内容就围绕Spring Boot Swagger 未授权访问漏洞展开,把原理、真实风险、检测方法、修复方案、以及修复过程中容易踩的坑,一次性讲透。适合正在用 Spring Boot 做后端开发的同学、做安全测试的同学,以及负责上线前安全把关的运维和测试同学。不管你是刚接触 Spring Boot 的新手,还是已经写过几个企业级项目的老手,这里面的排查思路和配置细节都能直接拿去用。

先说清楚一个概念:Swagger 未授权访问本身不是一个“漏洞编号”意义上的 CVE,它属于配置缺陷导致的信息泄露与攻击面扩大。单独看,它可能只是泄露接口结构;但结合其他弱点(比如某个接口鉴权缺失、参数校验不严),它就成了完整的攻击链入口。所以别觉得“不就是个文档页面嘛”,真正出问题的时候,它就是第一块倒下的多米诺骨牌。

2. Swagger 在 Spring Boot 里到底是怎么跑起来的

2.1 两代主流方案:springfox 与 springdoc-openapi

要理解漏洞,先得知道 Swagger 在 Spring Boot 项目里是怎么被“挂”上去的。目前市面上主要有两套:

  • springfox:老牌方案,对应 Swagger 2.x 规范,依赖springfox-swagger2springfox-swagger-ui。很多老项目、若依(RuoYi)早期版本用的就是它。它的典型特征是访问/swagger-ui.html/swagger-ui/index.html,元数据端点是/v2/api-docs
  • springdoc-openapi:新一代方案,对应 OpenAPI 3.x 规范,依赖springdoc-openapi-starter-webmvc-ui。Spring Boot 2.6+ 之后 springfox 兼容性问题频发,springdoc 逐渐成为主流。它的 UI 路径是/swagger-ui/index.html,元数据端点是/v3/api-docs

这两套方案的共同点是:只要依赖在 classpath 里,且没有显式关闭,Spring Boot 启动时就会自动注册一堆 HTTP 端点。这些端点默认不经过你的业务鉴权逻辑,因为它们是通过独立的Docket(springfox)或GroupedOpenApi(springdoc)配置注册的,走的是 Spring MVC 的RequestMappingHandlerMapping,而不是你自定义的拦截器链——除非你专门把它们也纳入拦截范围。

2.2 自动装配机制:为什么“引了依赖就暴露”

Spring Boot 的核心哲学是“约定优于配置”,自动装配(AutoConfiguration)是它的灵魂。Swagger 的 starter 里通常包含一个spring.factories(Spring Boot 2.7 之前)或AutoConfiguration.imports(2.7 之后)文件,声明了自动配置类。以 springdoc 为例,SpringDocConfiguration会在满足条件时自动创建OpenAPIBean 和相关的HandlerMapping

关键点在于:这些自动配置类默认是启用的。你只要在pom.xml里加了依赖,哪怕一行 Swagger 配置代码都没写,启动后访问/v3/api-docs也能拿到一份默认的接口描述。很多同学以为“我没写配置类,应该没开吧”,这是典型的误解。实测下来,只要依赖在,端点就在。

2.3 端点暴露的完整清单

不同方案暴露的路径不一样,我整理了一张对照表,方便你排查自己项目里到底开了哪些口子:

方案UI 页面路径元数据端点常见附加端点
springfox 2.x/swagger-ui.html/swagger-ui/index.html/v2/api-docs/swagger-resources/swagger-resources/configuration/ui
springdoc 1.x/swagger-ui/index.html/v3/api-docs/v3/api-docs/swagger-config/v3/api-docs.yaml
springdoc 2.x/swagger-ui/index.html/v3/api-docs同上,支持按 group 分组/v3/api-docs/{group}

注意:/swagger-resources这个端点经常被忽略,它会返回所有 Docket 分组的信息,即使你关了 UI,它也可能还在,等于间接泄露了接口分组结构。

2.4 为什么默认不鉴权:设计初衷与现实的错位

Swagger 的设计初衷是开发协作工具,面向的是内部开发、测试、前端联调场景。在这个语境下,它默认开放是合理的——大家本地跑,谁还去配鉴权。但现实是,很多项目把开发配置直接带到了生产环境,或者用同一套代码部署到公网,于是“内部工具”变成了“公开服务”。

更深层的原因是:Swagger 的端点注册在 Spring MVC 的 HandlerMapping 体系里,而大多数项目的鉴权是通过HandlerInterceptor或 Spring Security 的FilterChain实现的。拦截器的addPathPatterns如果只写了/api/**,那/v3/api-docs自然不在拦截范围内。这不是 Swagger 的锅,是配置边界没划清楚。

3. 未授权访问的真实风险:不只是“看到接口”

3.1 信息泄露:攻击者的侦察地图

最直接的后果是接口结构泄露。一份完整的 OpenAPI 描述里包含:所有路径、HTTP 方法、请求参数名与类型、请求体结构、响应结构、部分注解里的示例值、甚至枚举取值范围。对攻击者来说,这相当于拿到了一份带注释的 API 手册。

我做过一个实验:拿一个中等规模的 Spring Boot 项目(约 80 个接口),只凭/v3/api-docs返回的 JSON,就能推断出业务模块划分、数据库实体字段(因为 DTO 字段名往往和表字段对应)、以及哪些接口是管理类的(路径里带/admin/manage)。这些信息单独看不算敏感,但组合起来就是精准的攻击情报。

3.2 攻击面扩大:从“盲打”到“精准打击”

没有 Swagger 的时候,攻击者要猜路径、猜参数,成本高。有了 Swagger,他可以直接对着接口列表逐个测试。尤其是以下几类接口,一旦鉴权缺失,后果很严重:

  • 用户信息查询接口GET /api/user/{id}这类,如果没做越权校验,可以遍历 ID 拿全量用户数据。
  • 文件上传/下载接口:路径和参数一目了然,可能被用来上传恶意文件或下载敏感文件。
  • 管理后台接口:有些项目管理接口和业务接口在同一个应用里,Swagger 会把它们全列出来。
  • 调试/测试接口:开发阶段留下的/test/debug接口,忘了删,Swagger 一暴露就全露了。

3.3 结合其他漏洞的“化学反应”

单独一个 Swagger 未授权,可能只是中低危。但它经常和其他问题叠加:

  • 配合未授权接口:Swagger 告诉你有哪些接口,其中某个接口恰好没配鉴权,直接打通。
  • 配合参数注入:知道了参数名和类型,SQL 注入、命令注入的测试效率大幅提升。
  • 配合若依等框架的已知问题:热词里提到“若依 微服务 使用 swagger”,若依早期版本确实存在 Swagger 相关配置问题,攻击者熟悉框架结构后,利用成本更低。

3.4 合规视角:为什么安全扫描总报这个

很多安全扫描器(比如做“原理扫描”的那类工具)会把 Swagger 未授权访问列为中危或高危。原因很简单:它属于可被远程未授权访问的敏感信息端点。在等保、ISO 27001 这类合规检查里,接口文档暴露在公网通常会被判定为不符合“最小暴露原则”。所以不管从技术还是合规角度,上线前关掉它都是必要动作。

4. 检测与验证:怎么确认自己的项目有没有问题

4.1 手工快速验证:三条命令搞定

最直接的方法就是访问那几个已知路径。你可以用浏览器,也可以用 curl:

# springdoc 元数据 curl -s http://your-host:port/v3/api-docs | head -c 500 # springfox 元数据 curl -s http://your-host:port/v2/api-docs | head -c 500 # UI 页面 curl -s -o /dev/null -w "%{http_code}" http://your-host:port/swagger-ui/index.html

如果返回 200 且内容里有"openapi""swagger"字段,基本可以确认暴露了。返回 401/403 说明有鉴权拦截,返回 404 说明没开或路径不对。

4.2 自动化扫描思路:批量排查多个环境

如果你负责多个项目,手工一个个试太慢。可以写个简单的脚本,把常见路径列出来批量探测:

import requests paths = [ "/v3/api-docs", "/v2/api-docs", "/swagger-ui/index.html", "/swagger-ui.html", "/swagger-resources", "/v3/api-docs/swagger-config", ] targets = ["http://host1:8080", "http://host2:8080"] for target in targets: for path in paths: url = target + path try: r = requests.get(url, timeout=5, allow_redirects=False) if r.status_code == 200 and ("openapi" in r.text or "swagger" in r.text.lower()): print(f"[暴露] {url}") except Exception: pass

这个脚本只是演示思路,实际用的时候注意加并发控制和超时,别把目标打挂了。

4.3 从代码层面自查:依赖与配置双检查

光测端点还不够,最好从代码层面确认。检查两处:

  1. pom.xmlbuild.gradle:搜springfoxspringdocswagger关键字,确认是否引入了依赖。
  2. 配置类:搜@EnableSwagger2@EnableOpenApiDocketGroupedOpenApi,看有没有显式配置。如果有,看它的enable()或条件注解是否和生产环境绑定。

实操心得:我见过一个项目,pom.xml里依赖是<scope>provided</scope>,以为不会打包进去,结果部署时容器里恰好有那个 jar,还是暴露了。所以 scope 不是万能的,最终以运行时 classpath 为准。

4.4 常见误判:这些情况别当成漏洞

  • 返回 401/403:说明有鉴权,不算未授权访问,但要确认鉴权是否可绕过。
  • 返回 200 但内容是空 JSON:可能是配置了分组但没扫描到接口,风险较低,但仍建议关闭。
  • 内网环境:如果确认只在隔离内网、无外部访问路径,风险等级可以下调,但合规上仍建议生产关闭。

5. 修复方案:从“临时止血”到“根治”

5.1 方案一:生产环境直接关闭(推荐)

最彻底的做法是生产环境不启用 Swagger。有两种实现方式:

方式 A:用 Profile 控制依赖生效范围

pom.xml里把 Swagger 依赖限定在非生产 Profile:

<profiles> <profile> <id>dev</id> <dependencies> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.3.0</version> </dependency> </dependencies> </profile> </profiles>

打包时用mvn package -Pdev开发包带 Swagger,生产包用默认 Profile 不带。这样生产环境的 jar 里根本没有 Swagger 的类,端点自然不存在。

方式 B:用配置开关控制

springdoc 支持通过配置关闭:

# application-prod.properties springdoc.api-docs.enabled=false springdoc.swagger-ui.enabled=false

springfox 则通过 Docket 的enable(false)或配置项springfox.documentation.enabled=false控制。

注意:方式 B 只是不注册端点,但依赖和类还在 classpath 里。如果存在其他绕过方式(比如某些版本的条件判断缺陷),理论上仍有风险。所以安全要求高的场景,优先用方式 A。

5.2 方案二:加鉴权拦截(适合必须保留的场景)

有些团队确实需要生产环境保留 Swagger(比如给合作方看接口),那就必须加鉴权。核心思路是把 Swagger 端点纳入你的鉴权体系

如果是 Spring Security,可以这样配:

@Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth .requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } }

如果是自定义拦截器,记得把路径加进去:

registry.addInterceptor(authInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login", "/public/**");

实操心得:用addPathPatterns("/**")全拦截,再排除白名单,比只拦截/api/**更安全。因为后者容易漏掉 Swagger、Actuator 等非业务端点。热词里提到的 “metrics 未授权访问漏洞”“nacos namespaces 未授权访问漏洞” 本质上是同一类问题——端点没纳入统一鉴权。

5.3 方案三:网关层拦截(微服务场景)

微服务架构下,Swagger 可能分散在各个服务里。这时候在网关(如 Spring Cloud Gateway)统一拦截更高效:

spring: cloud: gateway: routes: - id: block-swagger uri: no://op predicates: - Path=/v3/api-docs/**,/swagger-ui/**,/v2/api-docs/** filters: - SetStatus=404

这段配置的意思是:匹配到 Swagger 路径的请求,直接返回 404,不转发到后端服务。这样即使某个服务忘了关,网关层也兜住了。

5.4 方案四:自定义路径 + 随机化(不推荐作为唯一手段)

有人会把 Swagger 路径改成随机字符串,靠“隐藏”来防护。这属于安全 through obscurity,只能提高一点门槛,不能作为唯一措施。因为路径可能通过前端 JS、日志、错误信息泄露。如果要用,也必须配合鉴权。

5.5 修复方案对比与选型建议

方案彻底性实施成本适用场景
Profile 隔离依赖生产不需要 Swagger
配置开关关闭快速止血
鉴权拦截生产需保留 Swagger
网关拦截微服务架构
路径随机化仅作辅助

我的建议是:生产环境优先用 Profile 隔离,从依赖层面根除;如果必须保留,用鉴权拦截 + 网关兜底双保险。

6. 修复过程中的坑与排查实录

6.1 关了 UI 但元数据还在

这是最常见的坑。很多人只关了swagger-ui,忘了api-docs。结果 UI 页面 404 了,但/v3/api-docs还能返回完整 JSON。修复时要成对关闭:

springdoc.api-docs.enabled=false springdoc.swagger-ui.enabled=false

6.2 配置了enabled=false但端点仍可访问

原因可能是配置没生效,常见于:

  • 配置文件没被加载(Profile 不对、文件名不对)。
  • 有多个配置源冲突(比如 Nacos 配置中心覆盖了本地配置)。
  • 版本差异导致配置项名称不同(springdoc 1.x 和 2.x 有细微差别)。

排查方法:启动时看日志里有没有springdoc相关的自动配置报告,或者用/actuator/env看实际生效的配置值。

6.3 Spring Security 放行了却还是 401

有时候配了permitAll但访问还是 401,可能是:

  • 请求被 CSRF 拦截了(GET 一般不会,但某些配置下会)。
  • 路径匹配写错了,比如/swagger-ui/**没覆盖/swagger-ui/index.html(实际上/**是覆盖的,但有人写成/swagger-ui/*就只匹配一层)。
  • 有多个SecurityFilterChain,优先级搞错了。

6.4 微服务下每个服务都要改,容易漏

微服务项目里,Swagger 配置往往散落在各个服务的application.yml里。建议:

  1. 把 Swagger 配置抽到公共依赖或配置中心,统一管理。
  2. 在 CI/CD 流水线里加一步检查:打包后扫描 jar 里有没有 Swagger 相关类,或者启动后自动探测端点。
  3. 网关层做统一拦截作为最后防线。

6.5 常见问题速查表

现象可能原因排查方向
UI 404 但 api-docs 200只关了 UI检查两个开关
配置关闭无效配置未加载/被覆盖看 actuator/env
加了鉴权仍可访问路径未纳入拦截检查拦截器路径匹配
网关拦截不生效路由优先级问题调整路由顺序
生产包仍有 Swagger依赖未隔离检查打包 Profile

独家避坑技巧:上线前用unzip -l your-app.jar | grep -i swagger检查 jar 里有没有 Swagger 相关类。如果有,说明依赖没隔离干净,需要回到 pom 层面处理。

7. 上线前的安全检查清单与长期防护

7.1 上线前必做的五项检查

  1. 依赖检查:确认生产包不含 Swagger 依赖,或已通过配置关闭。
  2. 端点探测:用 curl 或脚本探测/v3/api-docs/v2/api-docs/swagger-ui/index.html等路径,确认返回 404 或 401。
  3. 鉴权覆盖:确认所有非业务端点(Swagger、Actuator、Druid 监控页等)都纳入了鉴权或已关闭。
  4. 网关规则:微服务场景确认网关层有兜底拦截规则。
  5. 日志检查:启动日志里不应出现 Swagger 相关的端点注册信息。

7.2 把检查自动化:CI/CD 集成

手工检查容易漏,建议集成到流水线。比如在部署后的冒烟测试阶段加一段:

#!/bin/bash HOST=$1 for path in /v3/api-docs /v2/api-docs /swagger-ui/index.html; do code=$(curl -s -o /dev/null -w "%{http_code}" "$HOST$path") if [ "$code" = "200" ]; then echo "安全检查失败:$path 返回 200" exit 1 fi done echo "安全检查通过"

这样每次上线自动跑一遍,有问题直接阻断发布。

7.3 长期防护:建立端点资产清单

Swagger 只是众多“非业务端点”中的一类。同类问题还有 Actuator 的/actuator/env/actuator/heapdump,Druid 的/druid/index.html,Nacos 的命名空间接口等。建议团队建立一份端点资产清单,记录每个端点的用途、是否鉴权、生产是否开启。新引入依赖时,先查它会不会自动注册端点,再决定怎么处理。

7.4 版本升级的注意事项

springdoc 和 springfox 的版本更新有时会改变默认行为。比如某些版本默认关闭了某些端点,某些版本又调整了路径。升级依赖后,务必重新跑一遍端点探测。另外,Spring Boot 2.6+ 对路径匹配策略有调整(PathPatternParser),可能影响拦截器匹配结果,升级时也要注意。

7.5 团队协作层面的建议

技术手段之外,流程也很重要。我的经验是:

  • 在代码模板或脚手架里,默认就把 Swagger 配成“仅 dev 启用”。
  • Code Review 时把“生产配置是否关闭调试端点”列为检查项。
  • 安全扫描工具接入 CI,把 Swagger 未授权列为阻断项。

这样即使个别同学疏忽,流程也能兜住。

8. 写在最后:一点个人体会

做安全这些年,我发现一个规律:大部分严重问题,都不是因为用了多高深的技术,而是因为基础配置没做对。Swagger 未授权访问就是典型——它不涉及复杂的漏洞利用,就是一个“该关的没关”。但恰恰是这种“简单”的问题,最容易在赶工期、多环境切换、微服务拆分的过程中被忽略。

我自己踩过的坑是:早期做项目时,觉得“内网环境无所谓”,结果有一次内网被横向渗透,攻击者就是从一台机器的 Swagger 页面开始,摸清了整个内网的接口结构。从那以后,我养成了一个习惯:不管什么环境,调试类端点一律默认关闭,需要时再按需开启,并且开启时必须加鉴权。这个习惯帮我省了很多事后排查的麻烦。

如果你现在正在维护一个 Spring Boot 项目,建议花十分钟按上面的清单过一遍。尤其是那些部署在公网、或者虽然在内网但有多人共用的环境,别让一份接口文档成了别人进入你系统的第一把钥匙。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询