脆弱的类
你的服务程序不可能反序列化任意类的对象。为什么不能呢?简单的回答是:因为在服务器端的类路径中可能存有被骇客利用的脆弱类。这些类所包含的代码为骇客造就了拒绝服务(DOS)的条件,或者--在极端情况下--会允许骇客注入任意代码。
你可能会相信存在这种攻击的可能性,但考虑到一个典型的服务器端程序的类路径中存在太多的类,不仅包含你自己的代码,还包括Java核心类库,第三方的类库,以及其它的中间件或框架中的类库。另外,在应用程序的生命周期中,类路径可能会被改变,或者为了应对底层运行环境的变化,应用程序的类路径也可能被修改。当试图去利用这样的弱点时,通过传送多个序列化对象,骇客能够将这些操作组合到一块。
我应该强调一下,仅当满足如下条件时,服务才会解序列化一个恶意对象:
1. 恶意对象的类存在于服务器端的类路径中。骇客不可能随便地传递任意类的序列化对象,因为应用服务可能无法加载这个类。
2. 恶意对象的类要么是可序列化的,要么是可外部化的。(即,服务器端的这个类要实现java.io.Serializable或java.io.Externalizable)
另外,通过从序列化流中直接复制数据,在不调用构造器的情况下,解序列化操作就能产生对象树,所以骇客不可能执行序列化对象类的构造器中的Java代码。
但骇客还有其它途径在服务器端去执行代码。无论JVM在何时去解序列化一个对象,都将实现如下三个方法中的一个,都将调用并执行该方法中的代码:
1. 方法readObject(),当标准的序列化机制不适用时,开发者一般就会用到该方法。例如,当需要对transient成员变量进行赋值时。
2. 方法readResolve(),一般用于序列化单例对象。
3. 方法readExternal(),用于外部化对象。
所以,如果在你的类路径中存在着使用上述方法的类,你就必须意识到骇客可能会在远程调用这些方法。此类攻击在过往曾被用于破坏Applet安全沙箱;同样地,相同的攻击技术也可用于服务器端应用。
继续读下去,将会看到如何才能只允许应用服务对其期望的类的对象进行解序列化。
Java序列化二进制格式
一个对象被序列化之后,二进制数据将包含有元数据(指与数据的结构相关的信息,例如类的名称,成员的数量,以及成员的类型),及对象数据本身。我将以一个简单的Bicycle类作为例子,如清单1所示,该类包含三个成员变量(id,name和nbrWheels)以及与之对应的set与get方法。
清单1. Bicycle类
package com.ibm.ba.scg.LookAheadDeserializer;
private static final long serialVersionUID = 5754104541168320730L;
private int id;
private String name;
private int nbrWheels;
public Bicycle(int id, String name, int nbrWheels) {
this.id = id;
this.name = name;
this.nbrWheels = nbrWheels;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public int getNbrWheels() {
return nbrWheels;
}
public void setNbrWheels(int nbrWheels) {
this.nbrWheels = nbrWheels;
}
}
在一个清单1所示类的实例被序列化之后,其数据流如清单2所示:
清单2. Bicycle类的序列化流
000000: AC ED 00 05 73 72 00 2C 63 6F 6D 2E 69 62 6D 2E |········com.ibm.|
000016: 62 61 2E 73 63 67 2E 4C 6F 6F 6B 41 68 65 61 64 |ba.scg.LookAhead|
000032: 44 65 73 65 72 69 61 6C 69 7A 65 72 2E 42 69 63 |Deserializer.Bic|
000048: 79 63 6C 65 4F DA AF 97 F8 CC C0 DA 02 00 03 49 |ycle···········I|
000064: 00 02 69 64 49 00 09 6E 62 72 57 68 65 65 6C 73 |··idI··nbrWheels|
000080: 4C 00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F |L··name···Ljava/|
000096: 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 |lang/String;····|
000112: 00 00 00 00 00 01 74 00 08 55 6E 69 63 79 63 6C |·········Unicycl|
000128: 65 |e|
对上述数据应用标准的对象序列化流协议,你将看到如清单3所示的序列化对象:
清单3. 被序列化的Bicycle对象的细节
STREAM_MAGIC (2 bytes) 0xACED
STREAM_VERSION (2 bytes) 5
newObject
TC_OBJECT (1 byte) 0x73
newClassDesc
TC_CLASSDESC (1 byte) 0x72
className
length (2 bytes) 0x2C = 44
tex