Spec-Zone .ru
спецификации, руководства, описания, API
CONTENTS | PREV | NEXT Java Object Serialization Specification
version 6.0

Security in Object Serialization





APPENDIX A


Topics:


A.1 Overview

The object serialization system allows a bytestream to be produced from a graph of objects, sent out of the JavaTM environment (either saved to disk or transmitted over the network) and then used to recreate an equivalent set of new objects with the same state.

What happens to the state of the objects outside of the environment is outside of the control of the JavaTM system (by definition), and therefore is outside the control of the security provided by the system. The question then arises: once an object has been serialized, can the resulting byte array be examined and changed in a way that compromises the security of the Java program that deserializes it? The intent of this section is to address these security concerns.


A.2 Design Goals

The goal for object serialization is to be as simple as possible and yet still be consistent with known security restrictions; the simpler the system is, the more likely it is to be secure. The following points summarize the security measures present in object serialization:


A.3 Security Issues

Naive use of object serialization may allow a malicious party with access to the serialization byte stream to read private data, create objects with illegal or dangerous state, or obtain references to the private fields of deserialized objects. Implementors concerned with security should be aware of the following implications of serialization:


A.4 Preventing Serialization of Sensitive Data

Fields containing sensitive data should not be serialized; doing so exposes their values to any party with access to the serialization stream. There are several methods for preventing a field from being serialized:


A.5 Writing Class-Specific Serializing Methods

To guarantee that a deserialized object does not have state which violates some set of invariants that need to be guaranteed, a class can define its own serializing and deserializing methods. If there is some set of invariants that need to be maintained between the data members of a class, only the class can know about these invariants, and it is up to the class author to provide a deserialization method that checks these invariants.

Security-conscious implementors should keep in mind that a serializable class' readObject method is effectively a public constructor, and should be treated as such. This is true whether the readObject method is implicit or explicit. It is not safe to assume that the byte stream that is provided to the readObject method was generated by serializing a properly constructed object of the correct type. It is good defensive programming to assume that the byte stream is provided by an adversary whose goal is to compromise the object under construction.

This is important even if you are not worried about security; it is possible that disk files can be corrupted and serialized data be invalid. So checking such invariants is more than just a security measure; it is a validity measure. However, the only place it can be done is in the code for the particular class, since there is no way the serialization package can determine what invariants should be maintained or checked.

In version 1.4 of the JavaTM 2 SDK, Standard Edition, support was added for class-defined readObjectNoData methods (see Section 3.5, "The readObjectNoData Method"). Non-final serializable classes which initialize fields to non-default values should define a readObjectNoData method to ensure consistent state in the event that a subclass instance is deserialized and the serialization stream does not list the class in question as a superclass of the deserialized object. This may occur in cases where the receiving party uses a different version of the deserialized instance's class than the sending party, and the receiver's version extends classes that are not extended by the sender's version. This may also occur if the serialization stream has been tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a "hostile" or incomplete source stream


A.6 Guarding Unshared Deserialized Objects

If a class has any private or package private object reference fields, and the class depends on the fact that these object references are not available outside the class (or package), then either the referenced objects must be defensively copied as part of the deserialization process, or else the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared methods (introduced in version 1.4 of the JavaTM 2 SDK, Standard Edition) should be used to ensure unique references to the internal objects.

In the copying approach, the sub-objects deserialized from the stream should be treated as "untrusted input": newly created objects, initialized to have the same value as the deserialized sub-objects, should be substituted for the sub-objects by the readObject method. For example, suppose an object has a private byte array field, b, that must remain private:

    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException
    {
        s.defaultReadObject();

        b = (byte[])b.clone();

        if (<invariants are not satisfied>)
            throw new java.io.StreamCorruptedException();
    }
This issue is particularly important when considering serialization of immutable objects containing internal (necessarily private) references to mutable sub-objects. If no special measures are taken to copy the sub-objects during deserialization of the container object, then a malicious party with write access to the serialization stream may violate the container object's immutability by forging references to its mutable sub-objects, and using these references to change the internal state of the container object. Thus, in this case it is imperative that the immutable container class provide a class-specific deserialization method which makes private copies of each mutable component object it deserializes. Note that for the purpose of maintaining immutability, it is unnecessary to copy immutable component objects.

It is also important to note that calling clone may not always be the right way to defensively copy a sub-object. If the clone method cannot be counted on to produce an independent copy (and not to "steal" a reference to the copy), an alternative means should be used to produce the copy. An alternative means of copying should always be used if the class of the sub-object is not final, since the clone method or helper methods that it calls may be overridden by subclasses.

Starting in version 1.4 of the JavaTM 2 SDK, Standard Edition, unique references to deserialized array objects can also be ensured by using the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared methods, thus avoiding the complication, performance costs and memory overhead of defensive copying. The readUnshared and writeUnshared methods are further described in Section 3.1, "The ObjectInputStream Class" and Section 2.1, "The ObjectOutputStream Class".


A.7 Preventing Overwriting of Externalizable Objects

Objects which implement the Externalizable interface must provide a public readExternal method. Since this method is public, it can be called at arbitrary times by anyone with access to the object. To prevent overwriting of the object's internal state by multiple (illegal) calls to readExternal, implementors may choose to add checks to insure that internal values are only set when appropriate:

    public synchronized void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException
    {
        if (! initialized) {
            initialized = true;

            // read in and set field values ...
        } else {
            throw new IllegalStateException();
        }
    }


A.8 Encrypting a Bytestream

Another way of protecting a bytestream outside the virtual machine is to encrypt the stream produced by the serialization package. Encrypting the bytestream prevents the decoding and the reading of a serialized object's private state, and can help safeguard against tampering with stream contents.

Object serialization allows encryption, both by allowing classes to define their own methods for serialization and deserialization (inside which encryption can be used), and by adhering to the composable stream abstraction (allowing the output of a serialization stream to be channelled into another filter stream which encrypts the data).



CONTENTS | PREV | NEXT
Copyright © 2005 Sun Microsystems, Inc. All Rights Reserved.