March 23, 2004

读《Effective java 中文版》(57)

第56条:保护性地编写readObject方法
  编写一个类的readObject方法,相当于编写一个公有的构造函数,无论给它传递一个什么样的字节流,它都必须产生一个有效的实例。下面是缩写健壮的readObject方法的指导原则:


  • 对于对象引用域必须保持为私有的类,对“将被保存到这些域中的对象”进行保护性拷贝。非可变类的可变组件就属于这一类别。

  • 对于具有约束条件的类,一定要检查约束条件是否满足,如果不满足的话,则抛出一个InvalidObjectException异常。这些检查应跟在所有的保护性拷贝之后。

  • 如果在对象图被反序列化之后,整个对象图必须都是有效的,则应该使用ObjectInputValidation接口。

  • 无论是直接方式还是间接方式,都不要调用类中可被改写的方法。

  readResolve方法有可能取被用来替代保护性的readObject方法。

  不严格地说,readObject是一个“用字节流作为唯一参数”的构造函数。当面对一个人工伪造的字节流的时候,readObject产生的对象会违反它所属的类的约束条件。初步的方法,是在readObject方法进行约束性检查,如下例:
private void readObject(OjbectInputStream s) throws IOException, ClassNotFoundException{
s.defaultReadObject();
//Check that our invariants are satisfied
if(start.compareTo(end)>0) throw new InvalidObjectException(start+" after "+ end);
}
  对上述的防范仍可进行攻击:伪造一个字节流,这个字节流以一个有效的Period实例所产生的字节流作为开始,然后附加上两个额外的引用,指向Period实例中的两个内部私有Date域,攻击者通过引用攻击内部域。所以,当一个对象被反序列化的时候,对于客户不应该拥有的对象引用,如果哪个域包含了这样的对象引用,则必须要做保护性拷贝,这是非常重要的。如下例:
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException{
s.defaultReadObject();
start=new Date(start.getTime());
end=new Date(end.getTime());
if(start.compareTo(end)>0) throw new InvlaidObjectException(start+" after "+end);
}

Posted by Hilton at March 23, 2004 09:59 AM | TrackBack
Comments
Post a comment









Remember personal info?