如何防止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.總結與拓展

 所以,在項目開發中,我們要根據實際情況,選擇最安全的單例模式實現方式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM