What can be done to make the Java serialization mechanism more secure? Security principles we follow to make Java serialization Safe.__java

Source: Internet
Author: User
Tags object serialization serialization
Overview

Java serialization serialization, we should be not unfamiliar. Its main function is to transform the state of an object into a sequence of bytes to facilitate the persistence of objects or network transmission. The reverse is the reverse process of deserialization. All the developers have to do is implement the Serializable interface and then call the Objectoutputstream/objectinputstream Writeobject/readobject method, and the other work JVM will automatically do it for you.


Whether there is a security risk to the serialization capability acquired by implementing the Serializable interface. Since these byte sequences have been disconnected from the Java security system on the disk or the network, can we view and modify the serialized byte sequence, or even inject a malicious virus? Does the Java deserialization mechanism validate the established object to ensure its security and accuracy? If you think of these questions, I'm afraid the answer will disappoint you. The sequence of bytes in Java serialization is essentially plaintext. and the composition of the byte sequence has a very clear document to explain, you can try to use some hexadecimal text editing tools, such as Hexeditor to look at the contents of the object serialization, you can see a lot of private variables of the actual assignment. For a description of the byte sequence, you can refer to the object serialization stream protocol, which is not much to say. The focus of this article is on some Java-provided security mechanisms through which we can elevate the serialization/deserialization security index.


Before reading this article, it's a good idea to understand some of the basics of Java serialization.


Transient

The use of this keyword, we should be not unfamiliar. It is used to specify which variable is not serialized in a serializable object. If you have some sensitive information stored in your object, you don't want to be seen by others. Then declare the variable that holds this sensitive information as transient. As the following code example shows, there is a private variable _salary in the Employee class, and we want to ignore this sensitive information when serializing, which defines it as transient.

Import java.io.Serializable;

public class Employee implements Serializable {


	private static final long Serialversionuid = -7331553489509930824l;
  private String _name;
	private transient double _salary;
	Public Employee (String name,double salary) {
		this._name = name;
		This._salary = salary;
	}
	
	Public String toString () {return
		' Employee Name: ' + this._name + ' with salary ' + this._salary;
	}

}

Import java.io.*; public class Serializationtest {public void serialize () throws ioexception{employee em = new Employee ("Matt", 1
		0000);
		FileOutputStream fos = null;
		
		ObjectOutputStream oos = null;
			try {fos = new FileOutputStream ("Employee.save");
	        Oos = new ObjectOutputStream (FOS);
            System.out.println ("serialized-" + em.tostring ());
		Oos.writeobject (EM);
			}finally{try {oos.close ();
			catch (IOException e) {//TODO auto-generated catch block E.printstacktrace ();
		}} public void Deserialize () throws ClassNotFoundException, IOException {fileinputstream = null;
		ObjectInputStream ois = null;
            try {fis = new FileInputStream ("Employee.save");
            OIS = new ObjectInputStream (FIS);
            Employee E = (employee) ois.readobject ();
		SYSTEM.OUT.PRINTLN ("deserialized-" + e.tostring ());
			}finally{try {ois.close (); catch (IOException e) {//TODO Auto-generated Catch block E.printstacktrace (); }} public static void Main (string[] args) throws IOException, ClassNotFoundException {serializationtest
		st = new Serializationtest ();
		St.serialize ();

	St.deserialize ();
 }

}

The output is as follows
Serialized-employee Name:matt with salary 10000.0
Deserialized-employee Name:matt with salary 0.0

This indicates that the value of the _salary variable is not serialized.

Wirteobject & ReadObjectThe WriteObject and ReadObject methods are optional methods for classes that implement the Serializable interface. If implemented, it is invoked when serializing/deserializing. Otherwise, the default serialization/deserialization is executed. In both methods, you only need to care about the field fields of the class itself that the method is in, not the parent class or subclass. In both of these methods, we still need to call the Objectoutputstream/objectinputstream method Defaultwriteobject/defaultreadobject to perform the default serialization of Java. The deserialization process. As shown in the following example, the Serializationtest class has no change compared with the above example, so it is omitted. The employee class is as follows
Import java.io.Serializable;

public class Employee implements Serializable {


	private static final long Serialversionuid = -7331553489509930824l;
  private String _name;
	Private double _salary;
	Public Employee (String name,double salary) {
		this._name = name;
		This._salary = salary;
	}
	
    private void WriteObject (Java.io.ObjectOutputStream stream)
    throws Java.io.IOException
{

    _salary = _ Salary  * _name.hashcode ();  For example, you can use any encryption algorithm that you think is appropriate.
    Stream.defaultwriteobject ();
    System.out.println ("Customized WriteObject method called.");
}

private void ReadObject (Java.io.ObjectInputStream stream)
    throws Java.io.IOException, ClassNotFoundException
{
    stream.defaultreadobject ();

    _salary = _salary/_name.hashcode (); For instance only, you can use any decryption algorithm that you think is appropriate.

    System.out.println ("customized ReadObject method called.");
}

	
	Public String toString () {return
		' Employee Name: ' + this._name + ' with salary ' + this._salary;
	}

}


The output of the run Serializationtest class is as follows
Serialized-employee Name:matt with salary 10000.0 customized WriteObject method called
.
Customized ReadObject method called.
Deserialized-employee Name:matt with Salary 10000.0


Sealedobject & SignedobjectThe methods already mentioned above provide flexibility for us to manipulate local variables within a serialized object. But a simpler approach is to use the Javax.crypto.SealedObject and Java.security.SignedObject classes to encrypt the entire serialized stream. You may notice that these two classes are stored in different Java package, although they guarantee the authenticity and completeness of the objects, and others tend to put them together in the Java API design. It is said that the cause of this situation is caused by the United States restrictions on the export of cryptographic software related regulations. JCE (where Java cryptography Extension,sealedobject) was first designed, the US government demanded that the export of cryptographic software be subject to a similar license from an arms dealer. This is a digression. This is not the Java encryption to do too much introduction, just use Sealedobject to do an example description, so we can see that they can easily encrypt the serializable objects, so as to ensure information security. The code is as follows
Import java.io.Serializable;



public class Employee implements Serializable {


	private static final long Serialversionuid = -7331553489509930824l;
  private String _name;
	Private double _salary;
	Public Employee (String name,double salary) {
		this._name = name;
		This._salary = salary;
	}
    * * * By implementing the Writereplace method to automatically return an alternative Sealedobject object is not feasible, will cause stack overflow. Because Sealedobject will do a deep copy of the incoming object to be encrypted. This operation is accomplished by serialization. So, it will be recursive into a dead loop.
    *
		/* Private Object Writereplace () throws Java.io.ObjectStreamException
	{
		
	    sealedobject so = null;
	    try {
			so =  new Sealedobject (This, new NullCipher ());
		catch (Illegalblocksizeexception e) {
			//TODO Auto-generated Catch block
			e.printstacktrace ();
		} catch (IOException e) {
			//TODO auto-generated catch Block
			e.printstacktrace ();
		} 
		
		return so;
	
	
	}
	*
	
	/Public String toString () {return
		"Employee Name:" + This._name + "with salary" + This._salary;
	}
	

}
Import java.io.*;
Import java.security.InvalidKeyException;
Import Java.security.Key;

Import java.security.NoSuchAlgorithmException;
Import javax.crypto.BadPaddingException;
Import Javax.crypto.Cipher;
Import javax.crypto.IllegalBlockSizeException;
Import Javax.crypto.KeyGenerator;
Import javax.crypto.NoSuchPaddingException;
Import Javax.crypto.NullCipher;
Import Javax.crypto.SealedObject;


Import Javax.crypto.SecretKey;
	
	
	public class Serializationtest {private static Key _key = null; public void serialize () throws IOException, Illegalblocksizeexception, InvalidKeyException, NoSuchAlgorithmException,
		nosuchpaddingexception{Employee em = new Employee ("Matt", 10000);
		FileOutputStream fos = null;
		
		ObjectOutputStream oos = null;
			try {fos = new FileOutputStream ("Employee.save");
			
			Oos = new ObjectOutputStream (FOS);

            Keygenerator keygenerator = keygenerator.getinstance ("Desede");
	        _key = Keygenerator.generatekey (); Cipher Cipher = cipher.gEtinstance ("Desede");
			Cipher.init (Cipher.encrypt_mode, _key);
			Sealedobject so = new Sealedobject (em,cipher);
	        Oos.writeobject (SO);

		System.out.println ("serialized-" + em.tostring ());
			}finally{try {oos.close ();
			catch (IOException e) {e.printstacktrace (); }} public void Deserialize () throws ClassNotFoundException, IOException, Illegalblocksizeexception, Badpaddinge
		Xception, InvalidKeyException, NoSuchAlgorithmException, nosuchpaddingexception {fileinputstream FIS = null;
		ObjectInputStream ois = null;
            try {fis = new FileInputStream ("Employee.save");
            OIS = new ObjectInputStream (FIS);
            Sealedobject so = (sealedobject) ois.readobject ();
            Employee E = (employee) so.getobject (_key);
		SYSTEM.OUT.PRINTLN ("deserialized-" + e.tostring ());
			}finally{try {ois.close ();
			catch (IOException e) {e.printstacktrace (); }} public static void main(string[] args) throws IOException, ClassNotFoundException, Illegalblocksizeexception, Badpaddingexception, InvalidKeyException, NoSuchAlgorithmException, nosuchpaddingexception {serializationtest st = new SerializationTest ()
		;
		St.serialize ();

	St.deserialize ();
 }

}



As shown above, we primarily encrypt an object when we serialize it. When deserializing, you must get the key to use when serializing. In addition, I tried to implement the Writereplace method in the employee class to directly wrap the object to be serialized into Sealedobject for return, but there was a stack overflow. Because Sealedobject will do a deep copy of the incoming object to be encrypted. This operation is accomplished by serialization. So, it will cause infinite recursion until the stack overflows.

ValidationJava does not check the validity of deserialized objects during deserialization. Furthermore, once the object is serializable, it shows that the byte sequence corresponding to the object state can be separated from the Java security system. The key is that the serialized byte sequence is readable to the user, and is essentially plaintext. So in the case of deserialization, for security reasons, we'd better validate the resulting data. At this point, we need to implement the interface java.io.ObjectInputValidation so that we can define the callback function in deserialization to do the validation work. The code is as follows

Import java.io.InvalidObjectException;
Import java.io.ObjectInputValidation;

Import java.io.Serializable; public class Employee implements Serializable,objectinputvalidation {private static final long Serialversionuid =-73
	31553489509930824L;
	Private String _name;
	Private double _salary;
		Public Employee (String name,double salary) {this._name = name;
	This._salary = salary;
	Public String toString () {return ' Employee Name: + this._name + ' with salary ' + this._salary;
    } private void ReadObject (Java.io.ObjectInputStream stream) throws Java.io.IOException, ClassNotFoundException {
    Stream.defaultreadobject ();
    Stream.registervalidation (this, 0);
System.out.println ("Customized ReadObject method called.");  @Override public void Validateobject () throws Invalidobjectexception {System.out.println ("Validation object
		
		Deserialization. "); if (_salary < 0) throw new Invalidobjectexception ("The deserialized object is invalid. Salary can ' t be negative. ");
		
	Else System.out.println ("The deserialized object is valid.");


 }

}
Import java.io.*; public class Serializationtest {public void serialize () throws ioexception{employee em = new Employee ("Matt",
		10000);
		FileOutputStream fos = null;
		
		ObjectOutputStream oos = null;
			try {fos = new FileOutputStream ("Employee.save");
	        Oos = new ObjectOutputStream (FOS);
            System.out.println ("serialized-" + em.tostring ());
		Oos.writeobject (EM);
			}finally{try {oos.close ();
			catch (IOException e) {e.printstacktrace ();
		}} public void Deserialize () throws ClassNotFoundException, IOException {fileinputstream = null;
		ObjectInputStream ois = null;
            try {fis = new FileInputStream ("Employee.save");
            OIS = new ObjectInputStream (FIS);
            Employee E = (employee) ois.readobject ();
		SYSTEM.OUT.PRINTLN ("deserialized-" + e.tostring ());
			}finally{try {ois.close ();
			catch (IOException e) {e.printstacktrace (); }}} Public static void Main (string[] args) throws IOException, classnotfoundexception {serializationtest st = new Serializationt
		EST ();
		St.serialize ();


	St.deserialize ();
 }


}
The output is as follows
Serialized-employee Name:matt with salary 10000.0 customized ReadObject method called
.
Validation object after deserialization.
The deserialized object is valid.
Deserialized-employee Name:matt with Salary 10000.0

If you want to see the results of validation failures, you can put the code in the Serializationtest class
Employee EM  = new Employee ("Matt", 10000);

To
Employee EM  = new Employee ("Matt",-10000);

The output is as follows
Serialized-employee Name:matt with salary-10000.0
customized ReadObject method called.
Validation object after deserialization.
Exception in thread "main" Java.io.InvalidObjectException:The deserialized the object is invalid. Salary can ' t be negative.
	At Com.tr.serialization.validation.Employee.validateObject (employee.java:37) at
	java.io.objectinputstream$ Validationlist$1.run (objectinputstream.java:2206) at
	java.security.AccessController.doPrivileged (Native method) at
	java.io.objectinputstream$validationlist.docallbacks (objectinputstream.java:2202)
	at Java.io.ObjectInputStream.readObject (objectinputstream.java:357) at
	Com.tr.serialization.validation.SerializationTest.deSerialize (serializationtest.java:38) at
	Com.tr.serialization.validation.SerializationTest.main (serializationtest.java:55)


Say NO to serializationNow that serialization poses a lot of security problems, we don't have to. Do not implement serializable interface, how simple ah? But sometimes things are not as simple as we think. For example, we have a class Partimeemployee class inherits from the employee class, and the employee class implements the serializable interface. shown in the following class diagram

At this point, we don't want Parttimeemployee to be serialized.  So what do we do? We only need to throw an exception notserializableexception in the WriteObject and ReadObject methods of the Parttimeemployee class. The code is as follows
Import java.io.Serializable;
	public class Employee implements Serializable {private static final long serialversionuid = -7331553489509930824l;
	Private String _name;
	Private double _salary;
		Public Employee (String name,double salary) {this._name = name;
	This._salary = salary;
	Public String toString () {return ' Employee Name: + this._name + ' with salary ' + this._salary;

}} import java.io.NotSerializableException;
	
	public class Parttimeemployee extends Employee {private int working_days_each_month;
	
	Private double salary_each_hour;
		Public Parttimeemployee (String name, double salary) {super (name, salary); TODO auto-generated constructor stub} private void WriteObject (Java.io.ObjectOutputStream stream) throws Ja

va.io.IOException {throw new Notserializableexception ("This class is not serializable");} private void ReadObject (Java.io.ObjectInputStream stream) throws Java.io.IOException, ClassNotFoundException {throw New NOtserializableexception ("This class is not serializable");

 }

}

Import java.io.*; public class Serializationtest {public void serialize () throws ioexception{parttimeemployee em = new parttime
		Employee ("Matt", 10000);
		FileOutputStream fos = null;
		
		ObjectOutputStream oos = null;
			try {fos = new FileOutputStream ("Employee.save");
            Oos = new ObjectOutputStream (FOS);
            Oos.writeobject (EM);
		System.out.println ("serialized-" + em.tostring ());
			}finally{try {oos.close ();
			catch (IOException e) {//TODO auto-generated catch block E.printstacktrace ();
		}} public void Deserialize () throws ClassNotFoundException, IOException {fileinputstream = null;
		ObjectInputStream ois = null;
            try {fis = new FileInputStream ("Employee.save");
            OIS = new ObjectInputStream (FIS);
            Parttimeemployee e = (parttimeemployee) ois.readobject ();
		SYSTEM.OUT.PRINTLN ("deserialized-" + e.tostring ());
			}finally{try {ois.close ();catch (IOException e) {//TODO auto-generated catch block E.printstacktrace (); }} public static void Main (string[] args) throws IOException, ClassNotFoundException {serializationtes
		T st = new Serializationtest ();
		St.serialize ();


	St.deserialize ();
 }


}
The output is as follows
Exception in thread "main" Java.io.NotSerializableException:This class isn't serializable at Com.tr.serialization.no.Pa Rttimeemployee.writeobject (parttimeemployee.java:20) at Sun.reflect.NativeMethodAccessorImpl.invoke0 (Native method) at Sun.reflect.NativeMethodAccessorImpl.invoke (nativemethodaccessorimpl.java:39) at Sun.reflect.DelegatingMethodAccessorImpl.invoke (DELEGATINGMETHODACCESSORIMPL.JAVA:25) at Java.lang.reflect.Method.invoke (method.java:597) at Java.io.ObjectStreamClass.invokeWriteObject ( objectstreamclass.java:940) at Java.io.ObjectOutputStream.writeSerialData (objectoutputstream.java:1469) at Java.io.ObjectOutputStream.writeOrdinaryObject (objectoutputstream.java:1400) at Java.io.ObjectOutputStream.writeObject0 (objectoutputstream.java:1158) at Java.io.ObjectOutputStream.writeObject ( objectoutputstream.java:330) at Com.tr.serialization.no.SerializationTest.serialize (serializationtest.java:18) at Com.tr.serialization.no.SerializationTest.main (Serializationtest.java: 56)
 

Summary
The Java API also introduces the Externalizable interface in the JDK version 1.1, which enables users to fully control the serialization/deserialization process through the writeexternal and Readexternal methods. Of course, we can also do any processing of the field to meet security considerations. This interface is implemented with increased flexibility, but it means that the user is responsible for the serialization/deserialization process, increasing the user's complexity while the serialization/deserialization performance issue is more prominent and a concern.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.