zoukankan      html  css  js  c++  java
  • 如何防止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.总结与拓展

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

  • 相关阅读:
    tar解压包的时候出现错误 gzip: stdin: not in gzip format
    解决Ubuntu刚装好的时候su命令密码错误的问题
    如何将Ubuntu左边的面板放到底部
    解决VMware安装Ubuntu的过程中窗口过小无法看到按钮的问题
    无法对视图创建索引,因为该视图未绑定到架构
    Matlab当中size() length()等函数讲解
    解决Matlab当中for循环运行慢的问题
    SqlServer如何获取存储过程的返回值
    Linux的五个查找命令
    linux安装redis官方教程
  • 原文地址:https://www.cnblogs.com/lthIU/p/6240128.html
Copyright © 2011-2022 走看看