点击关注公众号,利用碎片时间学习
同学们,最近相信大家都看到了SpringCloudGateway
爆出相关漏洞的信息了,既然如此,你们还不抓紧修改自己的程序吗?即使你没涉及到此次的漏洞,我也建议来看下,技多不压身,也许你会学到你不知道的知识。
本文项目代码gitee地址:
https://gitee.com/wei_rong_xin/rob-necessities
一、什么是SpringCloudGateway?
开始讲解漏洞的时候,我们来简单了解下什么是SpringCloudGateway
?
我们来简单了解下什么是SpringCloudGateway
用于在Spring WebFlux
之上构建API网关,旨在提供一种简单而有效的方式来路由到 API,并为它们提供横切关注点。
上面说的很官方,不大好理解,其实通过我在日常的使用过程中,可以简单的描述下方便你们的理解:
最为前端的统一API入口,我也可以称之为BFF(banked-for-front)吧
作为请求动态代理,请求转发,路由过滤,类似于nginx的能力
作为服务的统一鉴权模块,比如JWT,用户权限验证等
全局路径请求白名单
全局QPS的速率配置
开发阶段整合swagger,作为接口开发文档
关于我们常用的功能,我就描述上面这么多了,当然其能力可不止于此。
二、漏洞描述
2.1 CVE-2022-22947 代码注入漏洞
危害等级: 超危
威胁类型: 远程
漏洞描述:当启用、暴露和不安全的 Gateway Actuator 端点时,使用 Spring Cloud Gateway 的应用程序容易受到代码注入攻击。远程攻击者可以发出恶意制作的请求,允许在远程主机上进行任意远程执行。
受影响版本
3.1.0
3.0.0 ~ 3.0.6
旧版本、不受支持的版本
解决方案:
受影响版本的用户应使用以下补救措施。
3.1.x 用户应升级到 3.1.1+。
3.0.x 用户应该升级到 3.0.7+。
如果不需要Actuator端点,则应通过
management.endpoint.gateway.enabled: false
禁用它。如果需要Actuator端点,则应使用
Spring Security
对其进行保护。
2.2 CVE-2022-22946 HTTP2 不安全的 TrustManager
危害等级: 中危
威胁类型: 本地
漏洞描述:使用配置为启用 HTTP2 且未设置密钥存储或受信任证书的 Spring Cloud Gateway
的应用程序,将被配置为使用不安全的 TrustManager。这使得网关能够使用无效或自定义证书连接到远程服务。
受影响版本
3.1.0
解决方案:受影响版本的用户应使用以下补救措施。
3.1.x 用户应升级到 3.1.1+。
三、扩展知识
相信对于springcloudgateway
不熟悉,或者首次接触的同学们,对于有些名词是比较蒙圈的,我这里针对这些做一个简单的整理,帮助大家了解和学习。
3.1 Actuator端点是什么?
SpringcloudGateway的端点官方文档位置:
https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#actuator-api
Springboot端点官方文档:
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints
actuator端点使您可以监视应用程序并与之交互。Spring Boot 包含许多内置端点,并允许您添加自己的端点。例如,health端点提供基本的应用程序健康信息。
您可以启用或禁用每个单独的端点并通过 HTTP 或 JMX 公开它们(使它们可以远程访问)。当端点被启用和公开时,它被认为是可用的。内置端点仅在可用时才会自动配置。大多数应用程序选择通过 HTTP 公开,其中端点的 ID 和前缀
/actuator
映射到 URL。例如,默认情况下,health端点映射到/actuator/health
.
通过前面的描述我们可以知道,其实除了SpringCloudGateway
,其他的springboot服务同样可以开启自己的端点,比如我们使用springbootAdmin
监控服务的健康状态。
为了方便大家理解和使用,我使用前面搭建的一套微服务,来给大家演示下,微服务源码地址放置在文章开篇处,我们通过http://localhost:8000/ + 端点地址进行访问
。
在前面的文章当中我们并没有开启网关的Actuator端点,下面通过以下的配置开启一下:
引入依赖:
<!-- SpringBoot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
增加以下配置
management:
endpoint:
gateway:
enabled: true
endpoints:
web:
exposure:
include: gateway
下面简单提供几个网关支持的端点api接口测试,方便大家学习,关于更多,请查看前面提供的官方地址。
查看网关全部路由
路径:/actuator/gateway/routes
[
{
"predicate": "Paths: [/rob-necessities-gateway/**], match trailing slash: true",
"metadata": {
"nacos.instanceId": null,
"nacos.weight": "1.0",
"nacos.cluster": "DEFAULT",
"nacos.ephemeral": "true",
"nacos.healthy": "true",
"preserved.register.source": "SPRING_CLOUD"
},
"route_id": "ReactiveCompositeDiscoveryClient_rob-necessities-gateway",
"filters": [
"[[RewritePath /rob-necessities-gateway/(?<remaining>.*) = '/${remaining}'], order = 1]"
],
"uri": "lb://rob-necessities-gateway",
"order": 0
}
]
根据路由id获取路由信息
路径:/actuator/gateway/routes/{id}
,如访问http://localhost:8000/actuator/gateway/routes/ReactiveCompositeDiscoveryClient_rob-necessities-gateway
[
{
"predicate": "Paths: [/rob-necessities-gateway/**], match trailing slash: true",
"metadata": {
"nacos.instanceId": null,
"nacos.weight": "1.0",
"nacos.cluster": "DEFAULT",
"nacos.ephemeral": "true",
"nacos.healthy": "true",
"preserved.register.source": "SPRING_CLOUD"
},
"route_id": "ReactiveCompositeDiscoveryClient_rob-necessities-gateway",
"filters": [
"[[RewritePath /rob-necessities-gateway/(?<remaining>.*) = '/${remaining}'], order = 1]"
],
"uri": "lb://rob-necessities-gateway",
"order": 0
}
]
查看所有路由的全局过滤器
路径:/actuator/gateway/globalfilters
,包含你自定义的过滤器
{
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@4aedac7f": 2147483647,
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@1be7b7de": 2147483647,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@2ed4e0e9": 2147483646,
"org.springframework.cloud.gateway.filter.GatewayMetricsFilter@57441348": 0,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@545c3628": -2147482648,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@5b0ddbcf": -1,
"org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@182934f2": -2147483648,
"org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@7ff167c4": 10100,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@6c92af74": 10000,
"org.springframework.cloud.gateway.filter.ForwardPathFilter@7f8bc54e": 0
}
查看路由网关过滤器工厂
路径:/actuator/gateway/routefilters
{
"[MapRequestHeaderGatewayFilterFactory@72bb3f3e configClass = MapRequestHeaderGatewayFilterFactory.Config]": null,
"[RemoveRequestHeaderGatewayFilterFactory@6a275836 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[SecureHeadersGatewayFilterFactory@7828111d configClass = SecureHeadersGatewayFilterFactory.Config]": null,
"[DedupeResponseHeaderGatewayFilterFactory@774f2d7f configClass = DedupeResponseHeaderGatewayFilterFactory.Config]": null,
"[RemoveResponseHeaderGatewayFilterFactory@3f41a1f3 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[AddRequestHeaderGatewayFilterFactory@22938166 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[SetResponseHeaderGatewayFilterFactory@718aa49a configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[RequestHeaderToRequestUriGatewayFilterFactory@513f8279 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[RedirectToGatewayFilterFactory@6c720765 configClass = RedirectToGatewayFilterFactory.Config]": null,
"[RequestHeaderSizeGatewayFilterFactory@23591a2c configClass = RequestHeaderSizeGatewayFilterFactory.Config]": null,
"[AddRequestParameterGatewayFilterFactory@58d85a00 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[ModifyResponseBodyGatewayFilterFactory@6ddd71cc configClass = ModifyResponseBodyGatewayFilterFactory.Config]": null,
"[SetRequestHostHeaderGatewayFilterFactory@184b8899 configClass = SetRequestHostHeaderGatewayFilterFactory.Config]": null,
"[RewriteResponseHeaderGatewayFilterFactory@4992e34f configClass = RewriteResponseHeaderGatewayFilterFactory.Config]": null,
"[RemoveRequestParameterGatewayFilterFactory@1a6864f0 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[SaveSessionGatewayFilterFactory@6dac64ea configClass = Object]": null,
"[PrefixPathGatewayFilterFactory@b4f7e1c configClass = PrefixPathGatewayFilterFactory.Config]": null,
"[SetPathGatewayFilterFactory@4a547f9d configClass = SetPathGatewayFilterFactory.Config]": null,
"[StripPrefixGatewayFilterFactory@33fa6a8b configClass = StripPrefixGatewayFilterFactory.Config]": null,
"[SetRequestHeaderGatewayFilterFactory@1dbb3001 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[RetryGatewayFilterFactory@1342c6e1 configClass = RetryGatewayFilterFactory.RetryConfig]": null,
"[AddResponseHeaderGatewayFilterFactory@62808e8d configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[RequestSizeGatewayFilterFactory@2d5cb059 configClass = RequestSizeGatewayFilterFactory.RequestSizeConfig]": null,
"[ModifyRequestBodyGatewayFilterFactory@5c26ab0a configClass = ModifyRequestBodyGatewayFilterFactory.Config]": null,
"[RewriteLocationResponseHeaderGatewayFilterFactory@d6de944 configClass = RewriteLocationResponseHeaderGatewayFilterFactory.Config]": null,
"[RewritePathGatewayFilterFactory@4374c051 configClass = RewritePathGatewayFilterFactory.Config]": null,
"[PreserveHostHeaderGatewayFilterFactory@6258ea84 configClass = Object]": null,
"[SetStatusGatewayFilterFactory@49a6b730 configClass = SetStatusGatewayFilterFactory.Config]": null
}
刷新路由缓存
路径:/actuator/gateway/refresh
,返回200。注意此方法是post。
![c8e8c9999c498a90eb90db11910a7528.png](https://i-blog.csdnimg.cn/blog_migrate/dca7e4471b2be31f5da1f487507e727e.png)
创建路由
路径:/gateway/routes/{id_route_to_create}
,post方法,成功响应体为空。
我们创建一个跳转掘金的路由,参数如下:
{
"id": "juejin",
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0":"/juejin"
}
}],
"filters": [
],
"uri":"https://juejin.cn/" ,
"order":1
}
需要住的是,接口是post,注意设置请求参数类型为applicaiton/json
,使用postman会其他接口工具访问接口:http://localhost:8000/actuator/gateway/routes/juejin
响应如下:
![9ca7fa188d6b80a31ec71b45d106f3fe.png](https://i-blog.csdnimg.cn/blog_migrate/1c98e542ce0c93c69fce2ab01adff536.png)
我们访问前面的路由查看接口,看看结果:http://localhost:8000/actuator/gateway/routes/juejin
{
"predicate": "Paths: [/juejin], match trailing slash: true",
"route_id": "juejin",
"filters": [],
"uri": "https://juejin.cn:443/",
"order": 1
}
直接访问http://localhost:8000/juejin
, 页面跳转如下:
![14301feae2b1ab847b68ed5c0e7c3123.png](https://i-blog.csdnimg.cn/blog_migrate/84763b8667cf777dc37ee2ef7377540b.png)
发现很多异常,因为网页的很多内容通过我们的路径转发不能正确获取,包括静态资源和接口等。
删除路径
路径:/gateway/routes/{id_route_to_delete}
,接口是delete
下面我们使用删除方法,将我们创建的路由删除,调用接口:http://localhost:8000/actuator/gateway/routes/juejin
结果如下:
![962e6ad08299634ab3666619448ebd51.png](https://i-blog.csdnimg.cn/blog_migrate/a78e98abc405666baa6c08fe720dff1e.png)
3.2 TrustManager是什么?
TrustManager
是java中的一个接口,自jdk1.4被引入:
package javax.net.ssl;
public interface TrustManager {
}
它是JSSE信任管理器的基本接口。
负责管理在做出信任决策时使用的信任材料,并负责决定是否应接受对等方提供的凭据。
通过使用TrustManagerFactory
或通过实现其中一个TrustManager
子类来创建的。
目前拥有已知子接口X509TrustManager
:
package javax.net.ssl;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public interface X509TrustManager extends TrustManager {
void checkClientTrusted(X509Certificate[] var1, String var2) throws CertificateException;
void checkServerTrusted(X509Certificate[] var1, String var2) throws CertificateException;
X509Certificate[] getAcceptedIssuers();
}
已知实现类X509ExtendedTrustManager
:
package javax.net.ssl;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public abstract class X509ExtendedTrustManager implements X509TrustManager {
public X509ExtendedTrustManager() {
}
public abstract void checkClientTrusted(X509Certificate[] var1, String var2, Socket var3) throws CertificateException;
public abstract void checkServerTrusted(X509Certificate[] var1, String var2, Socket var3) throws CertificateException;
public abstract void checkClientTrusted(X509Certificate[] var1, String var2, SSLEngine var3) throws CertificateException;
public abstract void checkServerTrusted(X509Certificate[] var1, String var2, SSLEngine var3) throws CertificateException;
}
关于具体用法本文不解释了,只要简单了解就好了,这个类对于我们平生工作极其不常用。
来源:juejin.cn/post/7072247144769388558
推荐:
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!