For instance control, prefer enum types to readResolve
What is instance control?
Instance control basically refers to
single instance of the class OR singleton design pattern .
Java 1.5 onwards we should always
prefer ENUM to create singleton instance. It is absolutely safe . JVM
guarantees that. All earlier mechanical of controlling instance to single are
already broken.
So in any interview you can confidently
say ENUM= provides perfect singleton implementation .
Now what is readResolve?
readResolve is nothing but a method
provided in serializable class . This method is invoked when serialized object
is deserialized. Through readResolve method you can control how instance will
be created at the time of deserialization.Lets try to understand some code
public class President {
private static final President
singlePresident = new President();
private President(){
}
}
This class generates single instance
of President class. singlePresident is static instance and same will be used
across.
But do you see it breaks anywhere?
Implement this class with
Serializable interface and our instance control strategy to single instance will
immediately fail. At deserialization time brand new instance will be created
thus we are no longer restricting single instance.
Is there a way to control that.. Let’s
try.
public class President implements
Serializable {
private static final President
singlePresident = new President();
private President(){
}
Private Object readResolve(){
return singlePresident;
}
}
What did we do?
We controlled the
deserialization mechanism through readResolve method. We are returning the same
instance what was created at the time of serialization Or when President was instantiated
at first place. So the instance remains the same.
So can we say readResolve() method
is sufficient to control the instance creation??
Lets have a deeper look :
We need to understand that before
readResolve() method returns singlePresident , an instance of President is
created during normal deserialization process. This method does not let it
escape instead override with singlePresident and that deserialized instance is
garbage collected in almost no time. Now how can an attacker misuse this
deserialization step involved in the process.
An attacker will make use of
serialized steam , create an Dummy class , will create an private instance referring
to s3erialized instance of President class.
Ok ok , lets get into that code ;
Public class Dummy {
Private static President impersonator;
Private President instance;
readResolve (){
instance=impersonator;
}
}
What is happening here. Attacker used
stream deserializing President instance , injected an instance of Dummy class in that.
Further Dummy class has an instance of President
class. So here we are having circularity. When deserialization will happen ,
instance of Dummy class being inside deserializing President class, readResolve()
method of Dummy class will be invoked first.
As you see in above code . What does
this method do in Dummy class.
This method is assigning deserializing instance
to static impersonator variable. So even after deserialization is complete
Dummy class have cleverly retained the instance of deserialized class in static
field impersonator.
So actually happened here
two
instances of President class are available , one by attacker impersonator and one original singlePresident
returned by readResolve() method of President class. So attacker could
successfully break our instance control to single.
That’s the reason Effective java
says For instance control, prefer enum
types to readResolve
as ENUM = perfect singleton implementation
Please comment for any question or
to discuss it further
Hi.. its great post full of original source of info . A most effective topic over here .
ReplyDeleteGood to read will bookmark it for visit again.
Keep up the good work !