前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringCloudGateway 出事了,你的服务中招了吗?

SpringCloudGateway 出事了,你的服务中招了吗?

作者头像
JavaFish
发布2022-03-15 13:49:29
9250
发布2022-03-15 13:49:29
举报

哈喽,小伙伴们好。我是狗哥,最近相信大家都看到了 SpringCloudGateway 爆出相关漏洞的信息了,既然如此,你们还不抓紧修改自己的程序吗?即使你没涉及到此次的漏洞,我也建议来看下,技多不压身,也许你会学到你不知道的知识。

本文项目代码 gitee 地址:

代码语言:javascript
复制
https://gitee.com/wei_rong_xin/rob-necessities

01 什么是 SpringCloudGateway?

开始讲解漏洞的时候,我们来简单了解下什么是 SpringCloudGateway?

我们来简单了解下什么是 SpringCloudGateway 用于在 Spring WebFlux 之上构建 API 网关,旨在提供一种简单而有效的方式来路由到 API,并为它们提供横切关注点。

上面说的很官方,不大好理解,其实通过我在日常的使用过程中,可以简单的描述下方便你们的理解:

  • 最为前端的统一 API 入口,我也可以称之为 BFF(banked-for-front)
  • 作为请求动态代理,请求转发,路由过滤,类似于 nginx 的能力
  • 作为服务的统一鉴权模块,比如 JWT,用户权限验证等
  • 全局路径请求白名单
  • 全局 QPS 的速率配置
  • 开发阶段整合 swagger,作为接口开发文档

关于我们常用的功能,我就描述上面这么多了,当然其能力可不止于此。

02 漏洞描述

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+。

03 扩展知识

相信对于 springcloudgateway 不熟悉,或者首次接触的同学们,对于有些名词是比较蒙圈的,我这里针对这些做一个简单的整理,帮助大家了解和学习。

3.1 Actuator 端点是什么?

SpringcloudGateway 的端点官方文档位置:

代码语言:javascript
复制
https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#actuator-api

Springboot 端点官方文档:

代码语言:javascript
复制
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints

actuator 端点使您可以监视应用程序并与之交互。Spring Boot 包含许多内置端点,并允许您添加自己的端点。例如,health 端点提供基本的应用程序健康信息。

您可以启用或禁用每个单独的端点并通过 HTTPJMX 公开它们(使它们可以远程访问)。当端点被启用和公开时,它被认为是可用的。内置端点仅在可用时才会自动配置。大多数应用程序选择通过 HTTP 公开,其中端点的 ID 和前缀 /actuator 映射到 URL。例如,默认情况下,health 端点映射到 /actuator/health

通过前面的描述我们可以知道,其实除了 SpringCloudGateway,其他的 springboot 服务同样可以开启自己的端点,比如我们使用 springbootAdmin 监控服务的健康状态。

为了方便大家理解和使用,我使用前面搭建的一套微服务,来给大家演示下,微服务源码地址放置在文章开篇处,我们通过 http://localhost:8000/ + 端点地址进行访问。

在前面的文章当中我们并没有开启网关的 Actuator 端点,下面通过以下的配置开启一下:

  • 引入依赖:
代码语言:javascript
复制
<!-- SpringBoot Actuator -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 增加以下配置
代码语言:javascript
复制
management:
  endpoint:
    gateway:
      enabled: true
  endpoints:
    web:
      exposure:
        include: gateway

下面简单提供几个网关支持的端点 api 接口测试,方便大家学习,关于更多,请查看前面提供的官方地址。

查看网关全部路由

路径:/actuator/gateway/routes

代码语言:javascript
复制
[
    {
        "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

代码语言:javascript
复制
[
    {
        "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,包含你自定义的过滤器

代码语言:javascript
复制
{
    "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

代码语言:javascript
复制
{
    "[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。

创建路由

路径:/gateway/routes/{id_route_to_create}post 方法,成功响应体为空。

我们创建一个跳转掘金的路由,参数如下:

代码语言:javascript
复制
{
      "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

响应如下:

我们访问前面的路由查看接口,看看结果:http://localhost:8000/actuator/gateway/routes/juejin

代码语言:javascript
复制
{
    "predicate": "Paths: [/juejin], match trailing slash: true",
    "route_id": "juejin",
    "filters": [],
    "uri": "https://juejin.cn:443/",
    "order": 1
}

直接访问 http://localhost:8000/juejin, 页面跳转如下:

发现很多异常,因为网页的很多内容通过我们的路径转发不能正确获取,包括静态资源和接口等。

删除路径

路径:/gateway/routes/{id_route_to_delete},接口是 delete

下面我们使用删除方法,将我们创建的路由删除,调用接口:http://localhost:8000/actuator/gateway/routes/juejin

结果如下:

图片

3.2 TrustManager 是什么?

TrustManagerjava 中的一个接口,自 jdk1.4 被引入:

代码语言:javascript
复制
package javax.net.ssl;

public interface TrustManager {
}

它是 JSSE 信任管理器的基本接口。

负责管理在做出信任决策时使用的信任材料,并负责决定是否应接受对等方提供的凭据。

通过使用 TrustManagerFactory 或通过实现其中一个 TrustManager 子类来创建的。

目前拥有已知子接口 X509TrustManager

代码语言:javascript
复制
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

代码语言:javascript
复制
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
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一个优秀的废人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01 什么是 SpringCloudGateway?
  • 02 漏洞描述
    • 2.1 CVE-2022-22947 代码注入漏洞
      • 2.2 CVE-2022-22946 HTTP2 不安全的 TrustManager
      • 03 扩展知识
        • 3.2 TrustManager 是什么?
        • 参考链接
        相关产品与服务
        API 网关
        腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档