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

来源:互联网 发布:淘宝有什么促销活动 编辑:程序博客网 时间:2024/06/05 16:28

2.序列化并不安全

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

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

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

模糊化序列化数据

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

为了“hook”序列化过程,我们将在Person上实现一个writeObject方法;为了“hook”反序列化过程,我们将在同一个类上实现一个readObject方法。重要的是这两个方法的细节要正确—如果访问修改方法、参数或名称不同于清单4中的内容,那么代码将不被察觉地失败,Person的age将暴露。

  1. 清单4.模糊化序列化数据  
  2. publicclassPerson  
  3. implementsjava.io.Serializable  
  4. {  
  5. publicPerson(Stringfn,Stringln,inta)  
  6. {  
  7. this.firstName=fn;this.lastName=ln;this.age=a;  
  8. }  
  9.  
  10. publicStringgetFirstName(){returnfirstName;}  
  11. publicStringgetLastName(){returnlastName;}  
  12. publicintgetAge(){returnage;}  
  13. publicPersongetSpouse(){returnspouse;}  
  14.  
  15. publicvoidsetFirstName(Stringvalue){firstName=value;}  
  16. publicvoidsetLastName(Stringvalue){lastName=value;}  
  17. publicvoidsetAge(intvalue){age=value;}  
  18. publicvoidsetSpouse(Personvalue){spouse=value;}  
  19.  
  20. privatevoidwriteObject(java.io.ObjectOutputStreamstream)  
  21. throwsjava.io.IOException  
  22. {  
  23. //"Encrypt"/obscurethesensitivedata  
  24. ageage=age<<2;  
  25. stream.defaultWriteObject();  
  26. }  
  27.  
  28. privatevoidreadObject(java.io.ObjectInputStreamstream)  
  29. throwsjava.io.IOException,ClassNotFoundException  
  30. {  
  31. stream.defaultReadObject();  
  32.  
  33. //"Decrypt"/de-obscurethesensitivedata  
  34. ageage=age<<2;  
  35. }  
  36.  
  37. publicStringtoString()  
  38. {  
  39. return"[Person:firstName="+firstName+  
  40. "lastName="+lastName+  
  41. "age="+age+  
  42. "spouse="+(spouse!=null?spouse.getFirstName():"[null]")+  
  43. "]";  
  44. }  
  45.  
  46. privateStringfirstName;  
  47. privateStringlastName;  
  48. privateintage;  
  49. privatePersonspouse;  

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

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

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

如果需要对整个对象进行加密和签名,最简单的是将它放在一个javax.crypto.SealedObject和/或java.security.SignedObject包装器中。两者都是可序列化的,所以将对象包装在SealedObject中可以围绕原对象创建一种“包装盒”。必须有对称密钥才能解密,而且密钥必须单独管理。同样,也可以将SignedObject用于数据验证,并且对称密钥也必须单独管理。结合使用这两种对象,便可以轻松地对序列化数据进行密封和签名,而不必强调关于数字签名验证或加密的细节。很简洁,是吧?