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.总结与拓展

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

  • 相关阅读:
    webdav srs相关
    How To Configure WebDAV Access with Apache on Ubuntu 14.04
    ubuntu 编译lighttpd
    srs编译及推流测试
    Compile pciutils (lspci, setpci) in Windows x86,在 Windows x86 平台下编译 pciutils (lspci, setpci)
    mingw MSYS2 区别
    Qt之美(三):隐式共享
    Qt之美(二):元对象
    Qt之美(一):d指针/p指针详解
    C++的栈空间和堆空间
  • 原文地址:https://www.cnblogs.com/lthIU/p/6240128.html
Copyright © 2011-2022 走看看