程序员面试系列之Java单例模式的攻击与防御
我写的程序员面试系列
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++ java Python
寻找下一个结点 牛客网 程序员面试金典 C++ java Python 题目描述 请设计一个算法,寻找二叉树中指定结点的下一个结点(即中序遍历的后继). 给定树的根结点指针TreeNode* root ...
- 碰撞的蚂蚁 牛客网 程序员面试金典 C++ Java Python
碰撞的蚂蚁 牛客网 程序员面试金典 C++ Java Python 题目描述 在n个顶点的多边形上有n只蚂蚁,这些蚂蚁同时开始沿着多边形的边爬行,请求出这些蚂蚁相撞的概率.(这里的相撞是指存在任意两只 ...
- 检查是否是BST 牛客网 程序员面试金典 C++ java Python
检查是否是BST 牛客网 程序员面试金典 C++ java Python 题目描述 请实现一个函数,检查一棵二叉树是否为二叉查找树. 给定树的根结点指针TreeNode* root,请返回一个boo ...
- Java程序员面试宝典1 ---Java基础部分(该博文为原创,转载请注明出处)
(该博文为原创,转载请注明出处 http://www.cnblogs.com/luyijoy/ by白手伊凡) 1. 基本概念 1) Java为解释性语言,运行过程:程序源 ...
- java程序员面试宝典之——Java 基础部分(1~10)
基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法. 1.一个".java"源文件中是否可以包 ...
- JAVA程序员面试宝典
程序员面试之葵花宝典 面向对象的特征有哪些方面 1. 抽象:抽象就是忽略一个主题中与当前目标2. 无关的那些方面,3. 以便更充分地注意与当前目标4. 有关的方面.抽象并不5. 打算了解全部问题 ...
- Java程序员面试之葵花宝典
程序员面试之葵花宝典 1.面向对象的特征有哪些方面 抽象:抽象就是忽略一个主题中与当前目标 无关的那些方面, 以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而 只是选择其中的一部 ...
- 两年Java程序员面试经验分享,从简历制作到面试总结!
前言 工作两年左右,实习一年左右,正式工作一年左右,其实挺尴尬的,高不成低不就.因此在面试许多公司,找到了目前最适合自己的公司之后.于是做一个关于面试的总结.希望能够给那些依旧在找工作的同学提供帮助. ...
- 《JAVA 程序员面试宝典(第四版)》读书笔记之前言
工作五年了一直在小的软件公司混,总感觉自己的专业知识没有太大的提升.当然了中间也换了一两家公司,面试的公司就很多家,总感觉正规的软件公司(无论大小)对于基础知识的考核都非常重视,而不管你说你之前服务过 ...
随机推荐
- codeforces 672A A. Summer Camp(水题)
题目链接: A. Summer Camp time limit per test 1 second memory limit per test 256 megabytes input standard ...
- I.MX6 system.img unpack repack
/************************************************************************* * I.MX6 system.img unpack ...
- SPOJ:Elegant Permuted Sum(贪心)
Special Thanks: Jane Alam Jan*At moment in University of Texas at San Antonio - USA You will be give ...
- 理解Objective-C Runtime (六)super
super 在Objective-C中,如果我们需要在类的方法中调用父类的方法时,通常都会用到super,如下所示: @interface MyViewController: UIViewContro ...
- Ubuntu 16.04 如何使用Samba服务器
对于Windows与Ubuntu之间的数据传输,我们习惯于使用FTP工具,不过还是有学员问到samba服务器搭建和使用的问题,这便是本文的来由. Ubuntu版本:ARM裸机1期加强版配套的Ubunt ...
- nodejs 全局对象 global
nodejs中有一个全局对象 global,所有的全局变量都是global对象的属性,glabal最根本的作用是作为全局变量的宿主, 全局变量: 1 在最外层定义的变量 2 全局对象的属性 3 隐式定 ...
- Task运行带参数的函数
Task<Int32> task = Task.Run(() => fun("s", 9)); 函数定义: private Int32 frun(string ...
- SPOJ FIBOSUM && FIBOSUM2
Fibonacci数列定义为 $$f_n = f_{n-1}+f_{n-2}, \text{以及初值}f_0=0, f_1=1.$$ 本文之讨论,皆在模$10^9+7$意义下. FIBOSUM 给定$ ...
- POJ3264【线段树】
求区间最值-(基础,继续) code---.. #include<cstdio> #include<iostream> #include<string.h> #in ...
- 洛谷CF1030F Putting Boxes Together(树状数组)
题意: 现在有n个物品,第i个物品他的位置在a[i],他的重量为w[i].每一个物品移动一步的代价为他的w[i].目前有2种操作: 1. x y 将第x的物品的重量改为y 2.l r 将编号在 [ l ...