美文网首页
单例模式安全问题--反射攻击

单例模式安全问题--反射攻击

作者: wbpailxt | 来源:发表于2019-11-24 18:21 被阅读0次

利用反射将私有构造器的权限打开

package com.geely.design.pattern.creational.singleton;

import java.io.Serializable;

/**
 * Created by geely
 */
public class HungrySingleton implements Serializable,Cloneable{
    // 准备阶段会被分配内存,但不会被赋予null值,在初始化阶段被初始化。
    private final static HungrySingleton hungrySingleton;

    static{
        hungrySingleton = new HungrySingleton();
    }

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
    private HungrySingleton(){
        
    }
    private Object readResolve(){
        return hungrySingleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getInstance();
    }
}
package com.geely.design.pattern.creational.singleton;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Created by geely
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        HungrySingleton instance = HungrySingleton.getInstance();
        Class objectClass = HungrySingleton.class;
        Constructor constructor = objectClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        //先进行HungrySingleton的初始化,再调用私有构造函数
        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();

        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);
    }
}

结果:

图片.png
由于HungrySingleton在类的初始化阶段就已经实例化了,当它调用构造函数constructor.newInstance()时,常量静态变量必定不为null。所以可以这样。
private HungrySingleton(){
        if(hungrySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
}

Test类

package com.geely.design.pattern.creational.singleton;
/**
 * Created by geely
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class objectClass = HungrySingleton.class;
        Constructor constructor = objectClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        //先进行HungrySingleton的初始化,再调用私有构造函数
        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
        System.out.println(newInstance);
 }
}

结果


图片.png

对于由静态内部类实现的单例模式也能够用同样的方式抵挡反射攻击。因为其在静态内部类初始化阶段就已经实例化好外部类。

package com.geely.design.pattern.creational.singleton;

/**
 * Created by geely
 */
public class StaticInnerClassSingleton {
    private static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }
    private StaticInnerClassSingleton(){
        if(InnerClass.staticInnerClassSingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
}

那对于不是在类加载的时候创建单例对象的情况该怎么处理?
先来看看像前面几个例子在构造函数判断单例对象是否为null会出现怎么样的情况。
单例类

package com.geely.design.pattern.creational.singleton;

/**
 * Created by geely
 */
public class LazySingleton {
    public static LazySingleton lazySingleton = null;
    private LazySingleton(){
        if(lazySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
    public  static LazySingleton getInstance(){
        synchronized(LazySingleton.class){
            if(lazySingleton == null){
                lazySingleton = new LazySingleton();
            }
            return lazySingleton;
        }
    }
}

测试类:

package com.geely.design.pattern.creational.singleton;
/**
 * Created by geely
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        LazySingleton newInstance = LazySingleton.getInstance();

        Class clazz = LazySingleton.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazySingleton instance = (LazySingleton) constructor.newInstance();
        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);

    }
}

结果:

图片.png
似乎是能抵御反射攻击,但这是因为在getInstance()方法中对静态变量lazySingleton进行了赋值操作,然后再在构造函数中对静态变量lazySingleton进行了判null操作。所以会抛出错误。
如果先调用私有构造函数,在调用静态方法getInstance()呢?
测试类:
package com.geely.design.pattern.creational.singleton;
/**
 * Created by geely
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = LazySingleton.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazySingleton instance = (LazySingleton) constructor.newInstance();

        LazySingleton newInstance = LazySingleton.getInstance();
        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);
    }
}

结果:


图片.png

这样就没有抵挡住反射攻击了。

相关文章

  • 单例模式安全问题--反射攻击

    利用反射将私有构造器的权限打开 结果: Test类 结果 对于由静态内部类实现的单例模式也能够用同样的方式抵挡反射...

  • 单例模式安全之反射攻击

    单例模式安全之反射攻击 源码 单例模式这里就不谈了,什么是单例模式可参考七种Java单例模式详解,这里是关于单例模...

  • 反射破坏单例模式(静态内部类)

    java反射,破坏单例模式 静态内部类 反射实例化单例

  • 单例模式的攻击之反射攻击

    上篇文章,我们讨论了序列化与反序列化对单例的破坏原理以及相应的抵御措施,本篇文章我们就讨论一下,反射对单例模式的破...

  • Singleton 单例模式

    饿汉式单例模式 饿汉式单例模式 通过静态代码块增加异常处理 懒汉式单例模式 存在线程安全问题 懒汉式单例模式 解决...

  • 反射(三)

    反射(三) 反射还可能会破坏单例模式,单例模式的特征: 私有化构造方法 提供全局唯一的公有访问点 以懒汉模式为例,...

  • 设计模式——单例模式的破坏

    概述: 之前学习了单例模式的几种实现,解决了多线程情况下,单例的线程安全问题,保证了单例的实现。但是单例模式在下面...

  • 单例模式之双重检查的演变

    前言 单例模式本身是很简单的,但是考虑到线程安全问题,简单的问题就变复杂了。这里讲解单例模式的双重检查。 单例模式...

  • C05 单例模式 Enum枚举单例(二) 抗反射攻击分析

    枚举单例抗反射攻击演示(一) NoSuchMethodException 报的是枚举类(EnumInstance ...

  • 05.单例模式(创建型)

    创建型模式-单例模式 一、饿汉式单利模式 饿汉式不需要考虑线程安全问题。 饿汉式比较浪费资源 二、懒汉式单例模式 ...

网友评论

      本文标题:单例模式安全问题--反射攻击

      本文链接:https://www.haomeiwen.com/subject/pzctwctx.html