关于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用于数据验证,并且对称密钥也必须单独管理。结合使用这两种对象,便可以轻松地对序列化数据进行密封和签名,而不必强调关于数字签名验证或加密的细节。很简洁,是吧?
让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用于数据验证,并且对称密钥也必须单独管理。结合使用这两种对象,便可以轻松地对序列化数据进行密封和签名,而不必强调关于数字签名验证或加密的细节。很简洁,是吧?
相关文章推荐
- 关于Java对象序列化您不知道的5件事
- [ZzDW] 关于Java对象序列化您不知道的5件事
- 关于Java对象序列化您不知道的5件事(1)
- 关于Java对象序列化您不知道的5件事(3)
- 关于Java对象序列化您不知道的5件事
- 关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- (转)关于 Java 对象序列化您不知道的 5 件事
- Java学习笔记(84)----------关于 Java 对象序列化你不知道的 5 件事
- 关于 Java 对象序列化的5件事
- 关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- 序列化--关于 Java 对象序列化您不知道的 5 件事
- 关于 Java 对象序列化您不知道的 5 件事
- 于Java对象序列化您不知道的5件事
- 深入洞见:你所不知道的Java 对象序列化的5件事儿