Reflection vulnerabilities and deserialization vulnerabilities in singleton patterns
In addition to the enumerative singleton mode, the remaining four are Singleton mode There are reflection vulnerabilities and deserialization vulnerabilities in the implementations of the mentioned singleton patterns.
package singleton; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; /** * Solving Singleton Patterns by Reflection and Deserialization * @author weiyx15 * */ public class SingletonCrack { public static void main(String[] args) throws Exception { // Normally create singleton objects SingletonLazy s1 = SingletonLazy.getInstance(); SingletonLazy s2 = SingletonLazy.getInstance(); System.out.println(s1); System.out.println(s2); // Solving a Single Case with Reflection Class<SingletonLazy> cls = (Class<SingletonLazy>) Class.forName("singleton.SingletonLazy"); // Get the SingletonLazy class Constructor<SingletonLazy> cons = cls.getDeclaredConstructor(null); // Constructing Method for Obtaining Singleton Lazy cons.setAccessible(true); // Visibility Check of Skipping Method SingletonLazy s3 = cons.newInstance(); // Call the constructor to generate a new object SingletonLazy s4 = cons.newInstance(); // Call the constructor to generate a new object System.out.println(s3); System.out.println(s4); // Solving singletons by deserialization FileOutputStream fos = new FileOutputStream("object.out"); // File Output Stream ObjectOutputStream oos = new ObjectOutputStream(fos); // Object Output Stream oos.writeObject(s1); // Serialize objects to files oos.close(); // Close object output stream fos.close(); // Close the file output stream FileInputStream fis = new FileInputStream("object.out"); // File input stream ObjectInputStream ois = new ObjectInputStream(fis); // Object input stream SingletonLazy s5 = (SingletonLazy) ois.readObject(); // Deserializing objects from files ois.close(); // Close the object input stream fis.close(); // Close File Input Stream System.out.println(s5); } }
Operation result
singleton.SingletonLazy@15db9742 // s1 singleton.SingletonLazy@15db9742 // s2 singleton.SingletonLazy@6d06d69c // s3 singleton.SingletonLazy@7852e922 // s4 singleton.SingletonLazy@3b07d329 // s5
As can be seen from the running results, the private construction method can be obtained by reflection, which instantiates two different object instances {@code singleton.Singleton Lazy@6d06d69c} and {@code singleton.Singleton Lazy@7852e922}. New objects {@code singleton.Singleton Lazy@3b07d329} can also be obtained by deserialization.
Taking the implementation of lazy singleton pattern as an example, the methods to solve reflection and deserialization vulnerabilities are as follows:
package singleton; import java.io.ObjectStreamException; import java.io.Serializable; /** * Lazy Singleton Model Eliminating Reflection Vulnerabilities and Deserialization Vulnerabilities * @author weiyx15 * */ public class SingletonLazySafe implements Serializable{ private static SingletonLazySafe instance; private SingletonLazySafe() { // Prevent reflection vulnerabilities by instantiating new instance s by calling private constructors again if (instance != null) { throw new RuntimeException(); // Throw a runtime exception } } public static synchronized SingletonLazySafe getInstance() { if (instance == null) // If not instantiated, instantiate first { instance = new SingletonLazySafe(); // Instantiate the object after calling the getInstance method } return instance; } /** * readResolve interface is called when reading an object from an I/O stream * Return instance object directly in readResolve interface * Avoid re-instantiating objects when deserializing * @return Singleton object * @throws ObjectStreamException */ private Object readResolve() throws ObjectStreamException { return instance; } }