怎样做才能让Java 序列化机制 更安全 ? Security principles we follow to make Java Serialization safe.
概述
Java 序列化 serialization,大家应该都不陌生。其主要职责就是将一个对象的状态转化为一个字节序列,以方便对象的持久化或网络传输。反序列化的过程正好相反。开发人员所要做的只是实现Serializable接口,然后调用ObjectOutputStream/ObjectInputStream的WriteObject/ReadObject方法即可,其他的工作 JVM 会自动帮你做了。
那通过实现Serializable 接口所获取的序列化能力是否有安全隐患?由于这些字节序列已经脱离了Java的安全体系存在于磁盘或网络上,我们能否对序列化后的字节序列进行查看和修改,甚至于注入恶意病毒呢? Java 反序列化机制是否又会对建立的对象进行验证以确保它的安全性、准确性呢? 如果你想到这些问题,那恐怕答案会让你失望了。Java序列化后的字节序列基本都是明文存在的,而且字节序列的组成有很明确的文档进行说明,你可以试着用一些十六进制的文本编辑工具,如Hexeditor 查看一下对象序列化后的内容,你都能看到很多私有变量的实际赋值。关于字节序列的说明,可参考对象序列化流协议 ,这里就不多说了。这篇文章的重点是说一些Java提供的安全机制,通过这些机制,我们能够提升序列化/反序列化的安全指数。
读这篇文章前,最好能了解一些Java序列化的基本知识。
这个关键字的用途,大家应该都不陌生。它用来指定可序列化对象中,哪个变量不被序列化。如果你的对象中存放了一些敏感信息,不想让别人看到的话。那么就把存放这个敏感信息的变量声明为Transient. 如下代码例子所示,Employee类中有一个私有变量_salary,我们在序列化时,想忽略这个敏感信息,那将它定义为transient即可。
这时候,我们不想让PartTimeEmployee被序列化。那我们该怎么办? 我们只需要在PartTimeEmployee类的writeObject和readObject方法中抛出异常NotSerializableException即可。代码如下Exception in thread "main" java.io.NotSerializableException: This class is not serializableat com.tr.serialization.no.PartTimeEmployee.writeObject(PartTimeEmployee.java:20)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)at java.lang.reflect.Method.invoke(Method.java:597)at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:940)at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)at com.tr.serialization.no.SerializationTest.serialize(SerializationTest.java:18)at com.tr.serialization.no.SerializationTest.main(SerializationTest.java:56)
总结
Java API在 JDK 1.1 的版本中还引入了 Externalizable 接口,通过实现该接口,用户可以通过WriteExternal和ReadExternal方法对序列化/反序列化的过程进行完全掌控,当然我们也可以对字段进行满足安全考虑的任何处理。实现该接口,灵活性增加了,但意味着用户要自己对序列化/反序列化的过程负责,增加了用户的复杂度,同时序列化/反序列化的性能问题是否会更突出,也是一个需要考虑的问题。