单例模式的反射漏洞和反序列化漏洞

本文探讨了单例模式在实现过程中可能遇到的反射漏洞和反序列化漏洞,并提供了具体的破解示例。针对这些问题,文章进一步介绍了如何通过修改构造方法和实现readResolve方法来加固懒汉式单例模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式的反射漏洞和反序列化漏洞

除了枚举式单例模式外,其余4种在单例模式提到的单例模式的实现方式都存在反射漏洞和反序列化漏洞。

package singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
 * 用反射和反序列化的方法破解单例模式
 * @author weiyx15
 *
 */
public class SingletonCrack {
	public static void main(String[] args) throws Exception
	{
		// 正常创建单例对象
		SingletonLazy s1 = SingletonLazy.getInstance();
		SingletonLazy s2 = SingletonLazy.getInstance();
		System.out.println(s1);
		System.out.println(s2);
		
		// 用反射破解单例
		Class<SingletonLazy> cls = (Class<SingletonLazy>) Class.forName("singleton.SingletonLazy");		// 获取SingletonLazy类
		Constructor<SingletonLazy> cons = cls.getDeclaredConstructor(null);		// 获取SingletonLazy的构造方法
		cons.setAccessible(true);			// 跳过方法的可见性检查
		SingletonLazy s3 = cons.newInstance();	// 调用构造方法生成新对象
		SingletonLazy s4 = cons.newInstance();	// 调用构造方法生成新对象
		System.out.println(s3);
		System.out.println(s4);
		
		// 用反序列化破解单例
		FileOutputStream fos = new FileOutputStream("object.out");	// 文件输出流
		ObjectOutputStream oos = new ObjectOutputStream(fos);		// 对象输出流
		oos.writeObject(s1);										// 向文件序列化对象
		oos.close();												// 关闭对象输出流
		fos.close();												// 关闭文件输出流
		
		FileInputStream fis = new FileInputStream("object.out");	// 文件输入流
		ObjectInputStream ois = new ObjectInputStream(fis);			// 对象输入流
		SingletonLazy s5 = (SingletonLazy) ois.readObject();		// 从文件反序列化对象
		ois.close();												// 关闭对象输入流
		fis.close();												// 关闭文件输入流
		System.out.println(s5);
	}
}

运行结果

singleton.SingletonLazy@15db9742	// s1
singleton.SingletonLazy@15db9742	// s2
singleton.SingletonLazy@6d06d69c	// s3
singleton.SingletonLazy@7852e922	// s4
singleton.SingletonLazy@3b07d329	// s5

从运行结果可以看到,通过反射可以得到私有构造方法,从而实例化两个不同的对象实例{@code singleton.SingletonLazy@6d06d69c}和{@code singleton.SingletonLazy@7852e922}. 通过反序列化,也可以得到新对象{@code singleton.SingletonLazy@3b07d329}.

以懒汉式单例模式的实现为例,解决反射漏洞和反序列化漏洞的方法如下:

package singleton;

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * 排除了反射漏洞和反序列化漏洞的懒汉式单例模式
 * @author weiyx15
 *
 */
public class SingletonLazySafe implements Serializable{
	private static SingletonLazySafe instance;
	
	private SingletonLazySafe() {
		// 防止反射漏洞通过再次调用私有构造方法实例化新的instance
		if (instance != null)
		{
			throw new RuntimeException();	// 抛出运行时异常
		}
	}
	
	public static synchronized SingletonLazySafe getInstance() {
		if (instance == null)	// 如果未实例化,则先实例化
		{
			instance = new SingletonLazySafe();	// 调用getInstance方法后再实例化对象
		}
		return instance;
	}
	
	/**
	 * 从I/O流读取对象时会调用readResolve接口
	 * 在readResolve接口中直接返回instance对象
	 * 避免反序列化时重新实例化对象
	 * @return 单例对象
	 * @throws ObjectStreamException
	 */
	private Object readResolve() throws ObjectStreamException {
		return instance;
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值