我写的程序员面试系列
Java面试系列-webapp文件夹和WebContent文件夹的区别?
程序员面试系列:Spring MVC能响应HTTP请求的原因?
Java程序员面试系列-什么是Java Marker Interface(标记接口)
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的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
相关推荐
【JAVA_C#程序员面试宝典3】中涵盖了多个IT领域的重要知识点,主要涉及Java和C#编程语言,面试常见问题,以及与Web开发相关的技术。以下是对这些知识点的详细解释: 1. **接口与抽象类的理解和区别**: - 接口...
《程序员面试宝典,典型例题解析》是针对计算机行业面试者的一份宝贵资源,它汇集了近年来各大知名计算机公司在面试过程中常出现的题目,并提供了详细的解答思路与方法。这份资料旨在帮助准备面试的程序员们熟悉面试...
在.NET与Java这两个主流的开发平台上,程序员的面试题通常会涉及广泛的技术领域和实践能力。对于准备在深圳求职的程序员来说,了解这些面试题可以帮助提升自身的竞争力。以下是对“程序员深圳面试题大全”中可能涉及...
5. **设计模式**:23种经典设计模式,如工厂模式、单例模式、观察者模式、装饰器模式等,以及如何在实际项目中灵活应用。 6. **编程语言特性**:针对特定语言(如Java、C++、Python、JavaScript等)的深入理解,...
了解常见的攻击手段(如SQL注入、XSS、CSRF等)和防御措施,以及加密算法如RSA、AES等是必备的。 7. 设计模式:设计模式是解决软件设计中常见问题的成熟方案,如单例模式、工厂模式、观察者模式等。面试中可能会...
3. 设计模式:熟悉常见的设计模式如单例、工厂、观察者、装饰器等,能提高代码质量和可维护性。 4. 框架应用:例如Spring Boot、Django、React等,了解其工作原理和应用场景。 二、网络技术 1. TCP/IP协议:理解TCP...
《剑指Offer》是一本针对程序员面试的经典指导书籍,它不仅涵盖了丰富的编程题库,还提供了详细的解题思路与源代码示例。对于即将步入职场或者希望提升自己编程能力的程序员来说,《剑指Offer》无疑是不可多得的学习...