如何防止JAVA反射對單例類的攻擊?

  在個人上篇隨筆中,咱們知道了建立單例類有如下幾種方式:java

  (1).餓漢式;安全

  (2).懶漢式(、加同步鎖的懶漢式、加雙重校驗鎖的懶漢式、防止指令重排優化的懶漢式);函數

  (3).登記式單例模式;測試

  (4).靜態內部類單例模式;優化

  (5).枚舉類型的單例模式。spa

在上面的5種實現方式中,除了枚舉類型外,其餘的實現方式是能夠被JAVA的反射機制給攻擊的,即便他的構造方法是私有化的,咱們也能夠作一下處理,從外部獲得它的實例。3d

  下面,我將會舉例來講明:code

 

說明:對象

  Singleton.java     沒有通過處理的餓漢式單例模式實現方式blog

  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.總結與拓展

 因此,在項目開發中,咱們要根據實際狀況,選擇最安全的單例模式實現方式。

相關文章
相關標籤/搜索