小李探花IU

导航

如何防止JAVA反射对单例类的攻击?

  在我的上篇随笔中,我们知道了创建单例类有以下几种方式:

  (1).饿汉式;

  (2).懒汉式(、加同步锁的懒汉式、加双重校验锁的懒汉式、防止指令重排优化的懒汉式);

  (3).登记式单例模式;

  (4).静态内部类单例模式;

  (5).枚举类型的单例模式。

在上面的5种实现方式中,除了枚举类型外,其他的实现方式是可以被JAVA的反射机制给攻击的,即使他的构造方法是私有化的,我们也可以做一下处理,从外部得到它的实例。

  下面,我将会举例来说明:

 

说明:

  Singleton.java     没有经过处理的饿汉式单例模式实现方式

  Singleton6.java   枚举类型的单例模式

  SingletonNotAttackByReflect.java         经过处理的饿汉式单例模式实现方式

  SingletonReflectAttack.java    具体反射类

  SingletonReflectAttackMain.java    JUnit测试类

举例1:不经过处理的单例类被JAVA反射机制攻击

Singleton.java    代码清单【1.1】

 1 public class Singleton 
 2 {
 3     private static boolean flag = true;
 4     private static final Singleton INSTANCE = new Singleton();
 5     
 6     private Singleton()
 7     {
 8     }
 9     
10     public static Singleton newInstance()
11     {
12         return INSTANCE;
13     }
14 
15 }

 

SingletonReflectAttack.java  代码清单【1.2】

 1 /**
 2      * 单例模式被java反射攻击
 3      * @throws IllegalArgumentException
 4      * @throws InstantiationException
 5      * @throws IllegalAccessException
 6      * @throws InvocationTargetException
 7      * @throws SecurityException
 8      * @throws NoSuchMethodException
 9      */
10     
11     public static void attack() throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException
12     {
13         Class<?> classType = Singleton.class;
14         Constructor<?> constructor = classType.getDeclaredConstructor(null);
15         constructor.setAccessible(true);
16         Singleton singleton = (Singleton) constructor.newInstance();
17         Singleton singleton2 = Singleton.newInstance();
18         System.out.println(singleton == singleton2);  //false
19     }

 

测试结果:SingletonReflectAttackMain.java  代码清单【1.3】

 1 /**
 2      * 1.测试单例模式被java反射攻击
 3      * @throws NoSuchMethodException 
 4      * @throws InvocationTargetException 
 5      * @throws IllegalAccessException 
 6      * @throws InstantiationException 
 7      * @throws SecurityException 
 8      * @throws IllegalArgumentException 
 9      */
10     @Test
11     public void testSingletonReflectAttack() throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
12     {
13         System.out.println("-------------单例模式被java反射攻击测试--------------");
14         SingletonReflectAttack.attack();
15         System.out.println("--------------------------------------------------");
16     }
17     

 

运行结果:

  返回结果为false,说明创建了两个不同的实例。通过反射获取构造函数,然后调用setAccessible(true)就可以调用私有的构造函数;所以创建出来的两个实例时不同的对象。

如果要抵御这种攻击,就要修改构造器,让他在被要求创建第二个实例的时候抛出异常。

下面,我们对饿汉式单例模式做修改。

举例2.经过处理的单例类,JAVA反射机制攻击测试

SingletonNotAttackByReflect.java   代码清单【2.1】

 1 package com.lxf.singleton;
 2 
 3 import javax.management.RuntimeErrorException;
 4 
 5 public class SingletonNotAttackByReflect
 6 {
 7     private static boolean flag = false;
 8     private static final SingletonNotAttackByReflect INSTANCE = new SingletonNotAttackByReflect();
 9     
10     //保证其不被java反射攻击
11     private SingletonNotAttackByReflect()
12     {
13         synchronized (SingletonNotAttackByReflect.class) 
14         {
15             if(false == flag)
16             {
17                 flag = !flag;
18             }
19             else
20             {
21                 throw new RuntimeException("单例模式正在被攻击");
22             }
23             
24         }
25     }
26     
27     public static SingletonNotAttackByReflect getInstance()
28     {
29         return INSTANCE;
30     }
31 
32     
33 }

 

 SingletonReflectAttack.java  代码清单【2.2】

 1 public static void modifiedByAttack() 
 2     {
 3         try 
 4         {
 5             Class<SingletonNotAttackByReflect> classType = SingletonNotAttackByReflect.class;
 6             Constructor<SingletonNotAttackByReflect> constructor = classType.getDeclaredConstructor(null);
 7             constructor.setAccessible(true);
 8             SingletonNotAttackByReflect singleton = (SingletonNotAttackByReflect) constructor.newInstance();
 9             SingletonNotAttackByReflect singleton2 = SingletonNotAttackByReflect.getInstance();
10             
11             System.out.println(singleton == singleton2); 
12         } 
13         catch (Exception e)
14         {
15             e.printStackTrace();
16         }
17         
18     }

 

SingletonReflectAttackMain.java  代码清单【2.3】

 1 /**
 2      * 2.修改后的单例模式被java反射攻击测试.
 3      * 攻击失败
 4      * @throws IllegalArgumentException
 5      * @throws SecurityException
 6      * @throws InstantiationException
 7      * @throws IllegalAccessException
 8      * @throws InvocationTargetException
 9      * @throws NoSuchMethodException
10      */
11     
12     @Test
13     public void testModifiedByattack() throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
14     {
15         System.out.println("-------------修改后的单例模式被java反射攻击测试--------------");
16         SingletonReflectAttack.modifiedByAttack();
17         System.out.println("----------------------------------------------------------");
18     }

 

运行结果:

在之前,我们也介绍过,枚举类型的单例模式也可以防止被JAVA反射攻击,这里我们简单测试一下。

举例3:枚举类型的单例模式被JAVA反射机制攻击测试

Singleton6.java    代码清单【3.1】

 1 public enum Singleton6
 2 {
 3     INSTANCE;
 4     
 5     private Resource instance;
 6     
 7     Singleton6()
 8     {
 9         instance = new Resource();
10     }
11     
12     public Resource getInstance()
13     {
14         return instance;
15     }
16     
17     
18 }

 

 

 SingletonReflectAttack.java  代码清单【3.2】

 1     public static void enumAttack() throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
 2     {
 3         try 
 4         {
 5             Class<Singleton6> classType = Singleton6.class;
 6             Constructor<Singleton6> constructor =(Constructor<Singleton6>) classType.getDeclaredConstructor();
 7             constructor.setAccessible(true);
 8             constructor.newInstance();
 9             
10         } 
11         catch (Exception e) 
12         {
13             e.printStackTrace();
14         }

 

SingletonReflectAttackMain.java  代码清单【3.3】

 1 /**
 2      * 枚举类型的单例模式被java反射攻击测试
 3      * 攻击失败
 4      * 
 5      * @throws IllegalArgumentException
 6      * @throws SecurityException
 7      * @throws InstantiationException
 8      * @throws IllegalAccessException
 9      * @throws InvocationTargetException
10      * @throws NoSuchMethodException
11      */
12     
13     @Test
14     public void testenumAttack() throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
15     {
16         System.out.println("-------------枚举类型的单例模式被java反射攻击测试--------------");
17         SingletonReflectAttack.enumAttack();
18         System.out.println("----------------------------------------------------------");
19     }

 

运行结果:

 

 4.总结与拓展

 所以,在项目开发中,我们要根据实际情况,选择最安全的单例模式实现方式。

 

posted on 2016-12-31 20:25  小李探花IU  阅读(8218)  评论(3编辑  收藏  举报