您的位置:首页 > 编程语言 > Java开发

关于Java对象序列化您不知道的5件事(2)

2013-09-05 09:04 330 查看
2.序列化并不安全

让Java开发人员诧异并感到不快的是,序列化二进制格式完全编写在文档中,并且完全可逆。实际上,只需将二进制序列化流的内容转储到控制台,就足以看清类是什么样子,以及它包含什么内容。

这对于安全性有着不良影响。例如,当通过RMI进行远程方法调用时,通过连接发送的对象中的任何private字段几乎都是以明文的方式出现在套接字流中,这显然容易招致哪怕最简单的安全问题。

幸运的是,序列化允许“hook”序列化过程,并在序列化之前和反序列化之后保护(或模糊化)字段数据。可以通过在Serializable对象上提供一个writeObject方法来做到这一点。

模糊化序列化数据

假设Person类中的敏感数据是age字段。毕竟,女士忌谈年龄。我们可以在序列化之前模糊化该数据,将数位循环左移一位,然后在反序列化之后复位。(您可以开发更安全的算法,当前这个算法只是作为一个例子。)

为了“hook”序列化过程,我们将在Person上实现一个writeObject方法;为了“hook”反序列化过程,我们将在同一个类上实现一个readObject方法。重要的是这两个方法的细节要正确—如果访问修改方法、参数或名称不同于清单4中的内容,那么代码将不被察觉地失败,Person的age将暴露。
清单4.模糊化序列化数据  
publicclassPerson  
implementsjava.io.Serializable  
{  
publicPerson(Stringfn,Stringln,inta)  
{  
this.firstName=fn;this.lastName=ln;this.age=a;  
}  
 
publicStringgetFirstName(){returnfirstName;}  
publicStringgetLastName(){returnlastName;}  
publicintgetAge(){returnage;}  
publicPersongetSpouse(){returnspouse;}  
 
publicvoidsetFirstName(Stringvalue){firstName=value;}  
publicvoidsetLastName(Stringvalue){lastName=value;}  
publicvoidsetAge(intvalue){age=value;}  
publicvoidsetSpouse(Personvalue){spouse=value;}  
 
privatevoidwriteObject(java.io.ObjectOutputStreamstream)  
throwsjava.io.IOException  
{  
//"Encrypt"/obscurethesensitivedata  
ageage=age<<2;  
stream.defaultWriteObject();  
}  
 
privatevoidreadObject(java.io.ObjectInputStreamstream)  
throwsjava.io.IOException,ClassNotFoundException  
{  
stream.defaultReadObject();  
 
//"Decrypt"/de-obscurethesensitivedata  
ageage=age<<2;  
}  
 
publicStringtoString()  
{  
return"[Person:firstName="+firstName+  
"lastName="+lastName+  
"age="+age+  
"spouse="+(spouse!=null?spouse.getFirstName():"[null]")+  
"]";  
}  
 
privateStringfirstName;  
privateStringlastName;  
privateintage;  
privatePersonspouse;  


如果需要查看被模糊化的数据,总是可以查看序列化数据流/文件。而且,由于该格式被完全文档化,即使不能访问类本身,也仍可以读取序列化流中的内容。

3.序列化的数据可以被签名和密封

上一个技巧假设您想模糊化序列化数据,而不是对其加密或者确保它不被修改。当然,通过使用writeObject和readObject可以实现密码加密和签名管理,但其实还有更好的方式。

如果需要对整个对象进行加密和签名,最简单的是将它放在一个javax.crypto.SealedObject和/或java.security.SignedObject包装器中。两者都是可序列化的,所以将对象包装在SealedObject中可以围绕原对象创建一种“包装盒”。必须有对称密钥才能解密,而且密钥必须单独管理。同样,也可以将SignedObject用于数据验证,并且对称密钥也必须单独管理。结合使用这两种对象,便可以轻松地对序列化数据进行密封和签名,而不必强调关于数字签名验证或加密的细节。很简洁,是吧?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: