反射攻击解决方案及原理
测试反射是否可以创建单例对象
- 创建测试类
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author lijiayin
*/
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = HungrySingleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
HungrySingleton instance = HungrySingleton.getInstance();
HungrySingleton object = (HungrySingleton) constructor.newInstance();
System.out.println(instance);
System.out.println(object);
System.out.println(instance == object);
}
}
-
测试结果
测试结果.png
- 结论
通过反射可以创建对象
反射攻击解决方案
构造器抛出异常,只适用于通过类初始化实现单例的模式
- 修改HungrySingleton类
import java.io.Serializable;
/**
* @author lijiayin
*/
public class HungrySingleton implements Serializable {
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton(){
if(hungrySingleton != null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
private Object readResolve(){
return hungrySingleton;
}
}
-
测试结果
测试结果.png
- 结论
成功阻止反射创建 - 同理StaticInnerClassSingleton这种单例方法,也可以通过这种方式阻止反射调用
/**
* @author lijiayin
*/
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){
if(InnerClass.staticInnerClassSingleton != null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
private static class InnerClass {
private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance(){
return InnerClass.staticInnerClassSingleton;
}
}
使用枚举类
创建EnumInstance枚举类型
/**
* @author lijiayin
*/
public enum EnumInstance {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getInstance(){
return INSTANCE;
}
}
测试序列化与反序列化
代码实现
import java.io.*;
/**
* @author lijiayin
*/
public class EnumSerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
EnumInstance enumInstance = EnumInstance.getInstance();
enumInstance.setData(new Object());
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("singleton_file"));
outputStream.writeObject(enumInstance);
File file = new File("singleton_file");
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
EnumInstance newInstance = (EnumInstance)inputStream.readObject();
System.out.println(enumInstance.getData());
System.out.println(newInstance.getData());
System.out.println(enumInstance.getData() == newInstance.getData());
}
}
测试结果
测试结果.png
原理
ObjectInputStream的源码,readObject()方法,与上一章类似,这次由于是enum类型,调用readEnum()方法,并没有创建新对象。
测试反射
代码实现
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author lijiayin
*/
public class EnumReflectTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = EnumInstance.class;
Constructor constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
constructor.setAccessible(true);
EnumInstance enumInstance = (EnumInstance) constructor.newInstance("ABC", 888);
}
}
测试结果
测试结果.png













网友评论