程序员面试系列之Java单例模式的攻击与防御

我写的程序员面试系列

Java面试系列-webapp文件夹和WebContent文件夹的区别?

程序员面试系列:Spring MVC能响应HTTP请求的原因?

Java程序员面试系列-什么是Java Marker Interface(标记接口)

使用JDK自带的工具jstack找出造成运行程序死锁的原因

编程面试题:编写一个会造成数据库死锁的应用

JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载

使用JavaScript ES6的新特性计算Fibonacci(非波拉契数列)

单例模式在很多Java程序员的眼中,应该是设计模式里最简单的一种了。那么单例模式可能会被攻击,您听说过么?

说到“单例模式被攻击”这个话题,大家最容易想到的可能就是通过序列化/反序列化来攻击单例模式,因为一个对象实例序列化再反序列化后,得到的新的对象虽然各字段内容和原字段一致,然而对象地址和原始对象地址相比已经发生了变化,因此它们是两个不同的对象。

上面的结论完全正确,然而除了序列化/反序列化,单例模式还可能遭受另一种方式的攻击,即反射攻击(Reflection attack)。

看一个具体例子:

public class JerrySingleton {

   private String name;

   private JerrySingleton(){

   name = "Jerry";

}

private static class SingletonHolder{

      private static final JerrySingleton INSTANCE = new JerrySingleton();

}

public static JerrySingleton getInstance() {

      return SingletonHolder.INSTANCE;

      }

}

上面是一个饿汉式单例。

然而我只需要将这个单例类JerrySingleton的构造函数通过反射设置成可以访问Accessible,然后就能通过反射调用该构造函数,进而生成新的对象实例。这样就破坏了单例模式。

Class<?> classType = JerrySingleton.class;

Constructor<?> c = classType.getDeclaredConstructor(null);

c.setAccessible(true);

JerrySingleton e1 = (JerrySingleton)c.newInstance();

JerrySingleton e2 = JerrySingleton.getInstance();

System.out.println(e1 == e2);

第6行代码会打印false。

针对这种攻击,一种可行的防御措施是在单例类的构造函数内定义一个布尔变量,初始化为false。当构造函数执行后,该变量被置为true。如果接下来构造函数再次被执行,则人为抛出异常,避免构造函数重复执行。

public class JerrySingletonImproved {

    private static boolean flag = false;

    private JerrySingletonImproved(){

         synchronized(JerrySingletonImproved.class) {

            if(flag == false) {

                  flag = !flag;

            }

      else {

              throw new RuntimeException("Singleton violated");

      }

  }

}

}

这种防御措施无法从根本上杜绝Singleton被攻击,因为攻击者仍旧可以通过反射来修改布尔变量flag的值,从而绕过这个检查。

最理想的不会受到攻击的单例模式实现是借助Java里枚举类Enumeration的特性:

这种实现类型的单例模式的消费代码:

System.out.println("Name:" + JerrySingletonAnotherApproach.INSTANCE.getName());

如果攻击者通过前面介绍的反射代码对这种实现方式的单例进行攻击,JDK会抛出NoSuchMethodException异常:

Exception in thread "main" java.lang.NoSuchMethodException: singleton.JerrySingletonAnotherApproach.<init>()

at java.lang.Class.getConstructor0(Class.java:3082)

at java.lang.Class.getDeclaredConstructor(Class.java:2178)

at singleton.SingletonAttack.test3(SingletonAttack.java:31)

at singleton.SingletonAttack.main(SingletonAttack.java:43)

究其原因,是因为现在我们是通过Java枚举方式实现的单例,枚举类没有传统意义上的构造函数,因此对这种反射攻击免疫。

要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java后端生活

JDBC(四)JDBC元数据和反射的利用

Java 通过JDBC获得连接以后,得到一个Connection 对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据...

7320
来自专栏GreenLeaves

SQL学习之用通配符进行数据过滤

一、Like操作符       之前介绍的所有的操作符都是针对已知值进行过滤。不管匹配一个值还是多个值,检验大于还是小于已知值,或者检查某个范围的值,其共同点是...

206100
来自专栏个人分享

MapReduce、Hbase接口API实践

读取hdfs中文件并做处理,取出卡号,通过卡号连接hbase查询出对应客户号,写入redis,因为不用输出,所以不调用context.write方法,整个操作在...

13720
来自专栏Ryan Miao

java设计模式(四)--单例模式

 Singleton最熟悉不过了,下面学习单例模式。转载:http://zz563143188.iteye.com/blog/1847029 单例对象(Sin...

32650
来自专栏任浩强的运维生涯

mongodb高级应用

一、  高级查询 查询操作符 条件操作符:db.collection.find({“field”:{$gt/$lt/$gte/$lte/$eq/$ne:valu...

375100
来自专栏Spark学习技巧

Hive : SORT BY vs ORDER BY vs DISTRIBUTE BY vs CLUSTER BY

在Apache Hive中,像SQL一样,您可以根据全局排序和分布要求决定对数据进行全局排序或局部排序。在这篇文章中,我们将了解Hive中的SORT BY,OR...

33750
来自专栏钟绍威的专栏

初学File类

对File类的基本方法的理解 今天刚开始学了File类 一开始看思想编程看得迷迷糊糊的,之后受不了了,直接去看API文档 归纳: File->jav...

202100
来自专栏ImportSource

来自JVM的一封ClassFile介绍信

我是一个class文件。我的内部是由一个被叫做ClassFile的structure组成。 我在jvm中占有很重要的地位,你可去看看jvm规范中我占了多少篇幅...

38480
来自专栏数据结构与算法

BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏(SG函数)

Description 小H和小Z正在玩一个取石子游戏。 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子, 每次取石子的个数有限制,谁不能...

30430
来自专栏ml

file类之目录

     可以解决的问题是:                 有时需要列出目录下指定类型的文件,比如java,txt等扩展名的文件,可以使用File类的下述两个...

30550

扫码关注云+社区

领取腾讯云代金券