The object serialization system allows a bytestream to be produced from a
graph of objects, sent out of the Java environment (either saved to disk or sent
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 Java 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, perhaps injecting viruses into Java programs? The intent of this
section is to address these security concerns.

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 how security in object
serialization has been implemented:

Only objects implementing the java.io.Serializable or java.io.Externalizable
interfaces can be serialized. there are mechanisms for not serializing certain
fields and certain classes.

The serialization package cannot be used to recreate the same object, and no
object is ever overwritten by a deserialize operation. All that can be done
with the serialization package is to create new objects, initialized in a
particular fashion.

While deserializing an object might cause code for the class of the object to
be loaded, that code loading is protected by all of the usual Java code
verification and security management guarantees. Classes loaded because of
deserialization are no more or less secure than those loaded in any other
fashion.

Externalizable objects expose themselves to being overwritten because the
readExternal method is public.

Direct handles to system resources, such as file handles, are the kind of
information that is relative to an address space and should not be written out
as part of an object's persistent state. Therefore, fields that contain this kind of
information should be declared transient, which prevents them from being
serialized. Note that this is not a new or overloaded meaning for the
transient keyword.

If a resource, like a file handle, was not declared transient, the object could
be altered while in its serialized state, enabling it to have improper access to
resources after it is deserialized.

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 writer to provide a
deserialization method that checks these invariants.

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.

Another way of protecting a bytestream outside the Java 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.

The implementation allows encryption, both by allowing the classes to have
their own special methods for serialization/deserialization and by using the
stream abstraction for serialization, so the output can be fed into some other
stream or filter.