网站首页 > java教程 正文
最近在看effective java看到一个反序列化炸弹,使用代码验证了下:
先写一个函数将对象序列化到文件中:
public static void saveObject(Object object,String file) throws Exception {
ObjectOutputStream out = null;
FileOutputStream fout = null;
try {
fout = new FileOutputStream(file);
out = new ObjectOutputStream(fout);
out.writeObject(object);
} finally {
fout.close();
out.close();
}
}
再写一个函数从文件中读取数据反序列化一个对象
public static Object readObject(String file) throws Exception {
ObjectInputStream in = null;
FileInputStream fin = null;
try {
fin = new FileInputStream(file);
in = new ObjectInputStream(fin);
Object object = in.readObject();
return object;
} finally {
fin.close();
in.close();
}
}
主函数如下,先构造一个对象,然后把这个对象序列化到文件中,在从文件反序列化对象:
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String file = "/Users/zhaoyc/10101010101.txt";
Bomb1 bomb = new Bomb1();
saveObject(bomb,file);
readObject(file);
}
对象的构造过程如下:
class Bomb implements Serializable{
private Set<Object> root;
public Bomb() {
root = new HashSet<>();
Set<Object> s1 = new HashSet<>();
Set<Object> s2 = new HashSet<>();
root.add(s1);
root.add(s2);
for(int i=0;i<100;i++) {
Set<Object> t1 = new HashSet<>();
Set<Object> t2 = new HashSet<>();
t1.add("bom");
s1.add(t1);
s1.add(t2);
s2.add(t1);
s2.add(t2);
s1 = t1;
s2 = t2;
}
}
}
这时候程序会一直运行,过段时间CPU飙升
,到底发生了什么?问题在于,反序列化 HashSet 实例需要计算其元素的散列码,这个例子中会计算多少次呢?构造的对象如下:
相当于计算了2的100次方,这已经是一个天文数字了
每次左边的集合中都放一个string 的图画出来感觉不美观,我又换了中对象生成方式,代码如下:
root = new HashSet<>();
Set<Object> s1 = new HashSet<>();
Set<Object> s2 = new HashSet<>();
System.out.println(s1.hashCode() == s2.hashCode());
System.out.println(s1.equals(s2));
System.out.println((new HashSet<String>()).equals(new HashSet<Integer>()));
root.add(s1);
root.add(s2);
for(int i=0;i<100;i++) {
Set<Object> t1 = new HashSet<>();
Set<Object> t2 = new HashSet<>();
s1.add(t1);
s1.add(t2);
s2.add(t1);
s2.add(t2);
s1 = t1;
s2 = t2;
}
s1.add("bom1");
s1.add("bom2");
s2.add("bom1");
s2.add("bom2");
生成的对象就行下面这样子:
然而运行后很快就结束了,难道反序列话的炸弹无效了么,试着调试了下这个对象的结构是这样子的:
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[bom2, bom1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
原来如此,其实这时候构造的对象是下面这样子的:
明明每次循环都是new了两个set的,为什么出现了一个呢?如下的代码:
仔细思考了下,感觉问题应该还是处在set的hashcode和equals上,试着运行了一下代码:
System.out.println(new HashSet().equals(new HashSet()));
System.out.println(new HashSet().hashCode() == new HashSet().hashCode());
不出所料结果都是true
跟踪了下源码发下其实在hashset的父类AbstractSet中重写了这两个方法:
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
如果仔细观察他们的实现会发现如果连个set的元素个数为空,那么他们的equals是返回true的,而且hashcode恒等于0,那如果是范型呢,试了下,如下:
System.out.println(new HashSet<String>().equals(new HashSet<Integer>()));
结果仍旧是true
猜你喜欢
- 2024-11-04 快速处理Kafka反序列化错误(kafka自定义反序列化)
- 2024-11-04 又一个反序列化漏洞,我服了...(反序列化漏洞修复方案)
- 2024-11-04 Java代码示例:如何使用 serialVersionUID处理序列化
- 2024-11-04 Java 序列化机制(java序列化过程)
- 2024-11-04 SpringBoot整合Grpc实现跨语言RPC通讯
- 2024-11-04 php和java及python3.10的序列化和反序列化
- 2024-11-04 Java修炼终极指南:133 避免在反序列化时发生DoS攻击
- 2024-11-04 聊聊fastjson反序列化的那些坑(fastjson反序列化原理)
- 2024-11-04 Java序列化 3 连问,这太难了吧(在线序列化工具)
- 2024-11-04 避免使用Java序列化(serializable 防止序列化)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)