一、背景
漏洞名称:Swagger API 未授权访问漏洞【原理扫描】
风险等级:中
详细描述:
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务,方便开发者快速了解和调试接口。但由于其文档默认公开在 Web 路径下,未经授权的用户也能访问,从而可能泄露接口信息,甚至造成未授权访问、任意文件读取等安全风险。
参考链接: https://www.sec-in.com/article/476
解决办法:
- 在生产环境中禁用 Swagger。
- 配置 Spring Security/Shiro 权限控制,限制 Swagger UI 相关 URL 的访问。
- 使用 nginx 或过滤器对相关接口进行访问控制。
二、问题分析与复现
部署的微服务项目中,访问 http://ip:port/swagger-resources
时,可以直接获取所有服务的 Swagger 接口信息。这说明接口文档未做访问权限控制,Swagger 相关接口在生产环境依然暴露,存在严重安全隐患。
项目架构使用了 spring-cloud-gateway
聚合多个微服务的 Swagger 接口,同时也集成了 swagger2(2.9.2)
和 knife4j(2.0.9)
。默认配置下,以下路径均可被外部访问:
/swagger-ui.html
/swagger-resources
/v2/api-docs
/doc.html
三、尝试过的禁用方法(未成功)
方法 1:使用 @Profile({"dev","test"})
注解
这种方式理论上可以控制 Swagger 配置类仅在开发或测试环境启用,但项目中 Swagger 路由配置是通过 RouterFunction
实现的,不是常规的 Docket
Bean 配置,导致 @Profile
注解未能控制实际路由注册,所以是无效滴。
方法 2:使用 @ConditionalOnProperty
通过设置:
@ConditionalOnProperty(name = "swagger.enable", havingValue = "true")
配合配置文件启用:
swagger:
enable: true
该控制同样只适用于基于 Docket
的 Swagger 配置类,无法影响 RouterFunction
中对 Swagger 接口的路由注册,所以这种方式还是无效滴。
方法 3:自定义权限拦截 Swagger 接口路径
尝试使用 Spring Security 拦截 /swagger-ui.html
和 /v2/api-docs
等路径,由于网关层的路由是通过 RouterFunction
注册的,绕过了 Spring Security 的传统 filter 链,权限控制并未生效,还是没解决 …
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs").hasRole("ADMIN")
.anyRequest().authenticated();
}
四、最终解决方案
结合实际架构,最终采用了配置开关 + 路由控制的方式。
修改 RouterFunctionConfiguration
代码
@Slf4j
@Configuration
@RequiredArgsConstructor
public class RouterFunctionConfiguration {
private final ImageCodeCheckHandler imageCodeCheckHandler;
private final ImageCodeCreateHandler imageCodeCreateHandler;
private final SwaggerResourceHandler swaggerResourceHandler;
private final SwaggerSecurityHandler swaggerSecurityHandler;
private final SwaggerUiHandler swaggerUiHandler;
@Value("${swagger.use-flag:false}")
private boolean useFlag;
@Bean
public RouterFunction<ServerResponse> routerFunction() {
RouterFunctions.Builder routerBuilder = RouterFunctions.route()
.route(RequestPredicates.path("/xxx/xxx").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
imageCodeCreateHandler)
.andRoute(RequestPredicates.POST("/xxx/xxx").and(RequestPredicates.accept(MediaType.ALL)),
imageCodeCheckHandler);
if (useFlag) {
routerBuilder
.andRoute(RequestPredicates.GET("/swagger-resources").and(RequestPredicates.accept(MediaType.ALL)),
swaggerResourceHandler)
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui").and(RequestPredicates.accept(MediaType.ALL)),
swaggerUiHandler)
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/security").and(RequestPredicates.accept(MediaType.ALL)),
swaggerSecurityHandler);
}
return routerBuilder.build();
}
}
配置文件 application-xxx.yml
(使用的是 nacos 配置中心)
knife4j:
# 开启增强配置
enable: false
# 启用生产环境屏蔽
production: true
swagger:
use-flag: false
title: EmpX Swagger API
# .....
其中:
knife4j.enable: false
禁用 Knife4j 前端增强功能knife4j.production: true
屏蔽生产环境访问swagger.use-flag: false
控制是否注册 Swagger 路由
验证:
gateway 项目部署后,访问以下路径均返回 404,Swagger 接口已彻底关闭:
/swagger-ui.html
/swagger-resources
/v2/api-docs
/doc.html
所以还是要根据实际的项目架构来调整,不一定是提供的方法不好用,可能是没用对或者根本就不适用…