十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot Swagger未授权访问漏洞:从检测到修复的完整指南

Spring Boot Swagger未授权访问漏洞:从检测到修复的完整指南 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-swagger2和springfox-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 端点。这些端点默认不经过你的业务鉴权逻辑因为它们是通过独立的Docketspringfox或GroupedOpenApispringdoc配置注册的走的是 Spring MVC 的RequestMappingHandlerMapping而不是你自定义的拦截器链——除非你专门把它们也纳入拦截范围。2.2 自动装配机制为什么“引了依赖就暴露”Spring Boot 的核心哲学是“约定优于配置”自动装配AutoConfiguration是它的灵魂。Swagger 的 starter 里通常包含一个spring.factoriesSpring Boot 2.7 之前或AutoConfiguration.imports2.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/uispringdoc 1.x/swagger-ui/index.html/v3/api-docs/v3/api-docs/swagger-config、/v3/api-docs.yamlspringdoc 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, timeout5, allow_redirectsFalse) if r.status_code 200 and (openapi in r.text or swagger in r.text.lower()): print(f[暴露] {url}) except Exception: pass这个脚本只是演示思路实际用的时候注意加并发控制和超时别把目标打挂了。4.3 从代码层面自查依赖与配置双检查光测端点还不够最好从代码层面确认。检查两处pom.xml或build.gradle搜springfox、springdoc、swagger关键字确认是否引入了依赖。配置类搜EnableSwagger2、EnableOpenApi、Docket、GroupedOpenApi看有没有显式配置。如果有看它的enable()或条件注解是否和生产环境绑定。实操心得我见过一个项目pom.xml里依赖是scopeprovided/scope以为不会打包进去结果部署时容器里恰好有那个 jar还是暴露了。所以 scope 不是万能的最终以运行时 classpath 为准。4.4 常见误判这些情况别当成漏洞返回 401/403说明有鉴权不算未授权访问但要确认鉴权是否可绕过。返回 200 但内容是空 JSON可能是配置了分组但没扫描到接口风险较低但仍建议关闭。内网环境如果确认只在隔离内网、无外部访问路径风险等级可以下调但合规上仍建议生产关闭。5. 修复方案从“临时止血”到“根治”5.1 方案一生产环境直接关闭推荐最彻底的做法是生产环境不启用 Swagger。有两种实现方式方式 A用 Profile 控制依赖生效范围在pom.xml里把 Swagger 依赖限定在非生产 Profileprofiles profile iddev/id dependencies dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.3.0/version /dependency /dependencies /profile /profiles打包时用mvn package -Pdev开发包带 Swagger生产包用默认 Profile 不带。这样生产环境的 jar 里根本没有 Swagger 的类端点自然不存在。方式 B用配置开关控制springdoc 支持通过配置关闭# application-prod.properties springdoc.api-docs.enabledfalse springdoc.swagger-ui.enabledfalsespringfox 则通过 Docket 的enable(false)或配置项springfox.documentation.enabledfalse控制。注意方式 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: - SetStatus404这段配置的意思是匹配到 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.enabledfalse springdoc.swagger-ui.enabledfalse6.2 配置了enabledfalse但端点仍可访问原因可能是配置没生效常见于配置文件没被加载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里。建议把 Swagger 配置抽到公共依赖或配置中心统一管理。在 CI/CD 流水线里加一步检查打包后扫描 jar 里有没有 Swagger 相关类或者启动后自动探测端点。网关层做统一拦截作为最后防线。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 上线前必做的五项检查依赖检查确认生产包不含 Swagger 依赖或已通过配置关闭。端点探测用 curl 或脚本探测/v3/api-docs、/v2/api-docs、/swagger-ui/index.html等路径确认返回 404 或 401。鉴权覆盖确认所有非业务端点Swagger、Actuator、Druid 监控页等都纳入了鉴权或已关闭。网关规则微服务场景确认网关层有兜底拦截规则。日志检查启动日志里不应出现 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/heapdumpDruid 的/druid/index.htmlNacos 的命名空间接口等。建议团队建立一份端点资产清单记录每个端点的用途、是否鉴权、生产是否开启。新引入依赖时先查它会不会自动注册端点再决定怎么处理。7.4 版本升级的注意事项springdoc 和 springfox 的版本更新有时会改变默认行为。比如某些版本默认关闭了某些端点某些版本又调整了路径。升级依赖后务必重新跑一遍端点探测。另外Spring Boot 2.6 对路径匹配策略有调整PathPatternParser可能影响拦截器匹配结果升级时也要注意。7.5 团队协作层面的建议技术手段之外流程也很重要。我的经验是在代码模板或脚手架里默认就把 Swagger 配成“仅 dev 启用”。Code Review 时把“生产配置是否关闭调试端点”列为检查项。安全扫描工具接入 CI把 Swagger 未授权列为阻断项。这样即使个别同学疏忽流程也能兜住。8. 写在最后一点个人体会做安全这些年我发现一个规律大部分严重问题都不是因为用了多高深的技术而是因为基础配置没做对。Swagger 未授权访问就是典型——它不涉及复杂的漏洞利用就是一个“该关的没关”。但恰恰是这种“简单”的问题最容易在赶工期、多环境切换、微服务拆分的过程中被忽略。我自己踩过的坑是早期做项目时觉得“内网环境无所谓”结果有一次内网被横向渗透攻击者就是从一台机器的 Swagger 页面开始摸清了整个内网的接口结构。从那以后我养成了一个习惯不管什么环境调试类端点一律默认关闭需要时再按需开启并且开启时必须加鉴权。这个习惯帮我省了很多事后排查的麻烦。如果你现在正在维护一个 Spring Boot 项目建议花十分钟按上面的清单过一遍。尤其是那些部署在公网、或者虽然在内网但有多人共用的环境别让一份接口文档成了别人进入你系统的第一把钥匙。
返回列表