CVE-2020-26945 mybatis二级缓存反序列化的分析与复现
字数 1575 2025-08-15 21:32:12

MyBatis二级缓存反序列化漏洞(CVE-2020-26945)分析与防御指南

1. MyBatis简介

MyBatis是一款优秀的持久层框架,最初是Apache的开源项目iBatis,2010年迁移到Google Code并更名为MyBatis。它支持:

  • 定制化SQL
  • 存储过程
  • 高级映射
  • 避免了几乎所有的JDBC代码
  • 手动设置参数和获取结果集
  • 使用简单XML或注解配置

2. 漏洞概述

CVE编号:CVE-2020-26945
发布日期:2020年10月6日
修复版本:MyBatis 3.5.6
漏洞类型:远程代码执行(RCE)
漏洞根源:二级缓存功能的反序列化安全问题

3. 漏洞利用条件

  1. 用户启用了MyBatis的二级缓存功能
  2. 攻击者能够修改缓存内容,替换为恶意反序列化数据
  3. 目标系统未设置JEP-290过滤机制
  4. 没有任何防御反序列化攻击的措施

4. 二级缓存机制详解

4.1 二级缓存工作原理

二级缓存将查询结果放入缓存中,下次查询相同条件时直接从缓存获取,降低SQL服务器压力。特点包括:

  • 默认不开启,需要手动配置
  • 可以缓存在Redis等KV数据库,也可自定义实现
  • 被缓存的对象必须实现Serializable接口

4.2 缓存接口定义

自定义缓存需要实现以下接口:

public interface Cache {
    String getId();
    int getSize();
    void putObject(Object key, Object value);
    Object getObject(Object key);
    boolean hasKey(Object key);
    Object removeObject(Object key);
    void clear();
}

4.3 启用二级缓存

  1. MyBatis配置文件中开启:
<settings>
    <setting name="cacheEnabled" value="true" />
</settings>
  1. Mapper XML中加入缓存标签:
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"/>

5. 漏洞分析

5.1 问题根源

当使用支持序列化的缓存实现(如Redis缓存)时:

  1. putObject方法会将对象序列化后存储
  2. getObject方法会从缓存中读取数据并反序列化
  3. 如果攻击者能控制缓存内容,可注入恶意序列化数据

5.2 Redis缓存实现示例

官方Redis缓存插件的关键代码:

@Override
public void putObject(final Object key, final Object value) {
    execute(new RedisCallback() {
        @Override
        public Object doWithRedis(Jedis jedis) {
            final byte[] idBytes = id.getBytes();
            jedis.hset(idBytes, key.toString().getBytes(), 
                      redisConfig.getSerializer().serialize(value));
            if (timeout != null && jedis.ttl(idBytes) == -1) {
                jedis.expire(idBytes, timeout);
            }
            return null;
        }
    });
}

@Override
public Object getObject(final Object key) {
    return execute(new RedisCallback() {
        @Override
        public Object doWithRedis(Jedis jedis) {
            return redisConfig.getSerializer().unserialize(
                jedis.hget(id.getBytes(), key.toString().getBytes()));
        }
    });
}

6. 漏洞复现步骤

6.1 环境搭建

  1. 使用示例项目:https://github.com/Lovelcp/spring-boot-mybatis-with-redis
  2. 修改ProductMapper.xml配置:
<mapper namespace="com.wooyoo.learning.dao.mapper.ProductMapper">
    <cache type="org.apache.ibatis.cache.impl.PerpetualCache"/>
    <select id="select" resultType="Product">
        SELECT * FROM products WHERE id = #{id} LIMIT 1
    </select>
    <update id="update" parameterType="Product" flushCache="true">
        UPDATE products SET name = #{name}, price = #{price} 
        WHERE id = #{id} LIMIT 1
    </update>
</mapper>
  1. 数据库配置(application.yml):
spring:
  datasource:
    url: jdbc:mysql://192.168.3.254/test?autoReconnect=true&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

6.2 攻击过程

  1. 访问正常端点(如http://127.0.0.1:9999/product/1)确认二级缓存生效
  2. 使用Redis缓存插件时,替换缓存数据为恶意序列化对象
  3. 当系统从缓存读取并反序列化该对象时,触发RCE

7. 防御措施

7.1 风险评估

  1. 确认业务是否使用二级缓存
  2. 检查缓存服务器是否对外暴露
  3. 评估第三方缓存是否使用Java反序列化

7.2 具体防御方案

  1. 升级MyBatis:升级到3.5.6或更高版本

  2. 网络隔离

    • 确保缓存服务器不对外网开放
    • 设置严格的网络访问控制
  3. 序列化防护

    • 实现自定义序列化器,增加安全校验
    • 使用白名单机制限制可反序列化的类
  4. JEP-290:启用JEP-290反序列化过滤器

  5. 缓存替代方案

    • 使用不依赖Java序列化的缓存实现
    • 考虑使用JSON等安全格式存储缓存数据
  6. 最小权限原则

    • 缓存服务使用最小必要权限运行
    • 数据库连接使用最小权限账户

8. 参考资源

  1. MyBatis官方修复PR:https://github.com/mybatis/mybatis-3/pull/2079
  2. 官方发布说明:https://blog.mybatis.org/
  3. JEP-290文档:https://openjdk.java.net/jeps/290

9. 总结

CVE-2020-26945漏洞的核心在于MyBatis二级缓存的反序列化机制可能被恶意利用。防御的关键在于:

  1. 控制缓存服务器的访问权限
  2. 避免不安全的反序列化操作
  3. 及时更新到安全版本
  4. 实施深度防御策略

通过合理配置和安全编码实践,可以充分利用MyBatis二级缓存的性能优势,同时避免安全风险。

MyBatis二级缓存反序列化漏洞(CVE-2020-26945)分析与防御指南 1. MyBatis简介 MyBatis是一款优秀的持久层框架,最初是Apache的开源项目iBatis,2010年迁移到Google Code并更名为MyBatis。它支持: 定制化SQL 存储过程 高级映射 避免了几乎所有的JDBC代码 手动设置参数和获取结果集 使用简单XML或注解配置 2. 漏洞概述 CVE编号 :CVE-2020-26945 发布日期 :2020年10月6日 修复版本 :MyBatis 3.5.6 漏洞类型 :远程代码执行(RCE) 漏洞根源 :二级缓存功能的反序列化安全问题 3. 漏洞利用条件 用户启用了MyBatis的二级缓存功能 攻击者能够修改缓存内容,替换为恶意反序列化数据 目标系统未设置JEP-290过滤机制 没有任何防御反序列化攻击的措施 4. 二级缓存机制详解 4.1 二级缓存工作原理 二级缓存将查询结果放入缓存中,下次查询相同条件时直接从缓存获取,降低SQL服务器压力。特点包括: 默认不开启,需要手动配置 可以缓存在Redis等KV数据库,也可自定义实现 被缓存的对象必须实现 Serializable 接口 4.2 缓存接口定义 自定义缓存需要实现以下接口: 4.3 启用二级缓存 MyBatis配置文件中开启: Mapper XML中加入缓存标签: 5. 漏洞分析 5.1 问题根源 当使用支持序列化的缓存实现(如Redis缓存)时: putObject 方法会将对象序列化后存储 getObject 方法会从缓存中读取数据并反序列化 如果攻击者能控制缓存内容,可注入恶意序列化数据 5.2 Redis缓存实现示例 官方Redis缓存插件的关键代码: 6. 漏洞复现步骤 6.1 环境搭建 使用示例项目:https://github.com/Lovelcp/spring-boot-mybatis-with-redis 修改ProductMapper.xml配置: 数据库配置(application.yml): 6.2 攻击过程 访问正常端点(如 http://127.0.0.1:9999/product/1 )确认二级缓存生效 使用Redis缓存插件时,替换缓存数据为恶意序列化对象 当系统从缓存读取并反序列化该对象时,触发RCE 7. 防御措施 7.1 风险评估 确认业务是否使用二级缓存 检查缓存服务器是否对外暴露 评估第三方缓存是否使用Java反序列化 7.2 具体防御方案 升级MyBatis :升级到3.5.6或更高版本 网络隔离 : 确保缓存服务器不对外网开放 设置严格的网络访问控制 序列化防护 : 实现自定义序列化器,增加安全校验 使用白名单机制限制可反序列化的类 JEP-290 :启用JEP-290反序列化过滤器 缓存替代方案 : 使用不依赖Java序列化的缓存实现 考虑使用JSON等安全格式存储缓存数据 最小权限原则 : 缓存服务使用最小必要权限运行 数据库连接使用最小权限账户 8. 参考资源 MyBatis官方修复PR:https://github.com/mybatis/mybatis-3/pull/2079 官方发布说明:https://blog.mybatis.org/ JEP-290文档:https://openjdk.java.net/jeps/290 9. 总结 CVE-2020-26945漏洞的核心在于MyBatis二级缓存的反序列化机制可能被恶意利用。防御的关键在于: 控制缓存服务器的访问权限 避免不安全的反序列化操作 及时更新到安全版本 实施深度防御策略 通过合理配置和安全编码实践,可以充分利用MyBatis二级缓存的性能优势,同时避免安全风险。