readResolve()bug?

时间:2010-03-06 23:13:26

标签: java serialization

Effective Java表示 readResolve仅在所有字段都是暂时的 时才有效。 这不是一个错误吗?为什么Java创建者会做这样的事情?

- 更新

抱歉,我的意思是More Effective Java,请参阅幻灯片30.

3 个答案:

答案 0 :(得分:2)

为完整起见,幻灯片29为:

  

项目77:流行测验:此类是单身吗?

public class Elvis implements Serializable {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
    private Object readResolve() {
        return INSTANCE;
    }
}

幻灯片30是:

  

答案:不幸的是

     

第一版超越了readResolve的力量

     

>猫王有一个非传染性的领域   (最爱的歌曲)
>巧妙   精心设计的攻击可以保存参考   反序列化
  猫王实例的时候   此字段已反序列化

     
      
  • 详情请见ElvisStealer(第77项)
  •   
     

> readResolve仅适用于   所有字段都是短暂的

第77项是:

  

项目77:对于实例控制,首选枚举类型为readResolve

     

...

     

如果是Elvis课程   实现Serializable,如下   readResolve方法就足够了   保证单身人士的财产:

// readResolve for instance control - you can do better!
private Object readResolve() {
  // Return the one true Elvis and let the garbage collector
  // take care of the Elvis impersonator.
  return INSTANCE;
}
     

此方法忽略反序列化   对象,返回尊贵   Elvis实例是在创建时创建的   该课程已初始化。因此,   猫王的序列化形式   实例不需要包含任何真实的   数据;所有实例字段都应该是   宣布短暂。事实上,如果你   例如,依赖于readResolve   控制,所有实例字段   对象引用类型必须是   宣称是暂时的。否则就是   确定的攻击者可能会这样做   保护对反序列化的引用   对象在readResolve方法之前   运行,使用的技术   与MutablePeriod模糊地相似   项目76中的攻击。<​​/ p>      

攻击有点复杂,但是   根本的想法很简单。如果一个   单身包含一个非瞬态   对象引用字段,内容   该字段将被反序列化   在单身人士的readResolve之前   方法运行。这样就可以了   制作流“偷”参考   原来反序列化的   当时单身人士的内容   对象引用字段是   反序列化。

如果所有字段都是瞬态的,那么这种情况不仅仅是有效的,如果所有对象引用都是瞬态的,那么它只是安全。您引用的幻灯片更进一步说是任何字段都是非瞬态的,攻击者可以获取对反序列化对象的引用。

至于为什么:人们可以争辩说Josh Bloch使用readResolve()作为单身人士是无意中使用了API,创作者在创作时并没有想到这一点。你也可以认为这只是一种不可预见的后果。但我不认为这是故意的弱点。

答案 1 :(得分:1)

我觉得你误解了Josh Bloch。来自java spec

  

对于Serializable和Externalizable   类,readResolve方法允许   替换/解析对象的类   在流之前从流中读取   返回给来电者。通过   实现readResolve方法,a   class可以直接控制类型   和它自己的实例的实例   被反序列化。

如你所见,这与你的陈述没什么相似。

我知道readResolve最典型的用例之一就是当你读取序列化的单例(或者你不想创建新实例的任何对象时,比如手动实现的枚举,即没有enum关键字)。在readResolve中,您可以读取字段(如果它们存在)并决定应该返回哪些已创建的对象(通常是单例)而不是创建新实例。

答案 2 :(得分:0)

使用readResolve()时,目标JVM中的Singleton行为无论如何都会被违反,因为readResolve()将返回对JVM中已存在的INSTANCE的引用。

但是在调用readResolve()之前将调用readObject(),并且在反序列化期间将使用JVM的反射创建Singleton类的另一个对象。 无论如何,这个对象可能会被垃圾收集,但是暂时目标JVM有两个相同类型的实例可以击败“单身”。