Java 中的序列化允许我们将对象转换为字节流。此字节流可以保存到磁盘或传输到另一个系统。反之,字节流可以反序列化并允许我们重新创建原始对象。
最大的问题在于反序列化部分。通常它看起来像这样:
ObjectInputStream in = new ObjectInputStream( inputStream );
return (Data)in.readObject();
在解码之前,无法知道您正在反序列化什么。攻击者可能序列化了一个恶意对象并将其发送到您的应用程序。一旦您调用readObject()
,恶意对象就已被实例化。您可能认为这些类型的攻击是不可能的,因为您需要在类路径上有一个易受攻击的类。但是,如果您考虑类路径上的类数量(包括您自己的代码、Java 库、第三方库和框架),则很可能存在一个易受攻击的类。
Java 序列化也被称为“不断给予的礼物”,因为它多年来产生了许多问题。Oracle 计划最终将 Java 序列化作为 Project Amber 的一部分删除。但是,这可能需要一段时间,并且不太可能在以前的版本中修复。因此,尽可能避免使用 Java 序列化是明智的。如果您需要serializable
在域实体上实现,最好实现自己的readObject()
,如下所示。这可以防止反序列化。
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Deserialized not allowed");
}
如果您需要自己反序列化输入流,则应使用有限制的。Apache Commons IOObjectsInputStream
就是一个很好的例子。这会检查反序列化的对象是否被允许。ValidatingObjectInputStreamObjectInputStream
FileInputStream fileInput = new FileInputStream(fileName);
ValidatingObjectInputStream in = new ValidatingObjectInputStream(fileInput);
in.accept(Foo.class);
Foo foo_ = (Foo) in.readObject();
对象反序列化问题并不局限于 Java 序列化。从 JSON 反序列化到 Java 对象也存在类似的问题。博客文章“Jackson 反序列化漏洞”中有一个关于 Jackson 库的反序列化问题的示例。