Ok, so here's where I discuss the how I've addressed serialization's security issues:

From the source code:
 // These two settings are to prevent DOS attacks.
    private static final int MAX_ARRAY_LEN = 32768;
    private static final int MAX_OBJECT_CACHE = 65664;

The output stream implementation automatically resets the cach, when it exceeds a certain size, while the ObjectInputStream throws a StreamCorruptedException if OBJECT_CACHE is exceeded.

@Override
    public void writeObjectOverride(Object obj) throws IOException {
    d.writeObject(obj);
    // This is where we check the number of Object's cached and
    // reset if we're getting towards half our limit.
    // One day this limit will become too small.
    if (d.numObjectsCached > 32768) reset();
    }

A new annotation

@AtomicSerial is provided for classes that implement Serializable and want to validate their invariants atomically. That is if invariants aren't satisfied, the object is not created and doesn't exist so cannot be used as an attack vector.

An annotation was chosen since it is not inherited by subclasses.

Classes that implement Serializable, still continue to do so as usual, the serial form doesn't change.

Use of the new streams is determined by MethodConstraints.

Child classes in the stream that don't implement @AtomicSerial (apart from instances of Throwable, immutable Object versions of primitives and MarshalledObject) require DeSerializationPermission.

Because circular links are prohibited, when a conventional Serializable object is constructed, it is not published until after it's readObject, readObjectNoData or readResolve method has been called, so an attacker is prevented from obtaining a reference to it through the stream. These object's are still vulnerable to finalizer attacks, however that requires downloaded code and the intent is for this to be used to establish proxy trust prior to granting DownloadPermission.

@AtomicSerial objects are constructed using a constructor:

SomeObject(GetArg arg) throws IOException{
    super(check(arg));
    Object somefield = arg.get("somefield", null);
}

Where GetArg extends GetFields, but provides caller sensitive methods, to ensure different classes can't see each others field namespaces. To construct a GetArg instance requires SerializablePermission("enableSubclassImplementation").

The lowest extension class in the inheritance heirarchy is called first, it checks its invariants using a static method, each class in the inheritance heirarchy checks it's invariants before Object's default constructor is called, if any invariant checks fail the object is not constructed.

This is also generic parameter and final field friendly.

@ReadInput (used to annotate a static method that returns ReadObject (an interface) are both provided to allow classes to gain direct access to the stream, as they would in readObject(ObjectInputStream in), but without requiring their own object instance.

Conventional classes are deserialized using a best effort approach, by trying each constructor on the lowest extension class, using default values. Not all Serializable object's can be constructed.

The intent is for services, proxy's and discovery to use DOS safe code to serialize their state while establishing trust.

Collection, List, Set, SortedSet, Map and SortedMap are replaced in the ObjectOutputStream implementation with DOS attack safe immutable versions backed by arrays, these are functional package private implementations, intended as parameters, so implementations of @AtomicSerial are required to pass them as parameters to their preferred collection implementation. @AtomicSerial implementations are encouraged to use checked collections, see java.util.Collections.

Conventional serialization can be used for trusted connections.

@AtomicSerial is very easy to implement, and much easier to evolve than default serialization, some users may wish to use it anyway (it's also final field friendly), but it is definitely intended to be optional.

Regards,

Peter.



On 8/02/2015 6:11 PM, Peter Firmstone wrote:
Thanks Dan, hopefully I don't dissapoint.

... So continuing on, another benefit of secure Serialization, if you're a lookup service, you don't need to authenticate your clients, you can deal with anyone and your not subject to DOS attacks, other than more conventional attacks unrelated to java.

I've been investigating Serialization security, identifying issues and considering how to deal with them. I think everyone is aware Serialization has security pitfalls, if I fail to mention an issue you're aware of, please let me know.

At first I thought it was just an issue of limiting the classes that could be deserialized and that's relatively easily done, for example, ArrayList reads an integer from the stream, then uses it to create an array, without sanity checking it first. Well that's easy, just prevent ArrayList and a bunch of others from deserializing...

Not so fast, ObjectInputStream also creates arrays, without sanity checking, blockdata also has similar issues during byte array creation.

So you only need send an Integer.MAX_VALUE to bring the jvm to it's knees, and if that doesn't do it, send a multi dimension array with a few more. It requires very little data from the sender, just a few bytes and a couple of integers.

In addition ObjectInputStream caches objects, so they don't have to be re-serialized, but if ObjectOutputStream doesn't perform a reset, well you can figure that out without my help.

But wait there's more...

During deserialization, Serializable objects are instantiated by calling a zero arg constructor of the first non Serializable super class, this partially constructed Object, an instance of a Serializable child class, without any invariant checks or validation, is then allocated an integer handle and published, an attacker is now free to obtain a reference to the unconstructed object simply by inserting a reference handle into the stream.

At this time the ProtectionDomain's of the classes in the object's heirarchy are not present in the AccessControlContext, which is why attackers in the past have been able to creat ClassLoader instances and download code, when someone has deserialized into privileged context (that renders DownloadPermission useless, because the attacker can work around it).

After unsafe publication, the fields are read in from the stream from super class to child class and set.

Now I don't blame the developers back in the day, they had deadlines and targets to achieve, but the market has changed significantly since then and the issue needs addressing.

The good news is, the Serialization stream protocol has all the components necessary to create a secure ObjectInputStream.

For example, the stream cache can be reset periodically, this also means we can place a limit on the number of objects cached, and require ObjectOuputStream to call reset. If it doesn't and the object cache exceeds our limit, StreamCorruptedException.

For array length, again we can impose limits, and throw StreamCorruptedException if the limit is exceeded.

The collections themselves aren't hard to solve, simply replace all collection types with a safe replacement in ObjectOutputStream and require DeSerializationPermission for known insecure objects.

Circular links, can't be supported using existing deserialization mechanisms.

Does this last point matter?

My implementation of ObjectInputStream doesn't support circular links and passes all lookup service tests. In this case circular links are replaced with null. To support circular links safely would require some cooperation from the classes participating in deserialization.

Construction during deserialization is the last challenge, many existing Serializable classes don't have public constructors or zero arg constructors, even though implementing Serializable is equivalent to a public constructor.

... to be continued, until next time.

Cheers,

Peter.


On 5/02/2015 2:38 AM, Dan Rollo wrote:
Very interesting. Looking forward to the next episode.

On Feb 4, 2015, at 9:11 AM, dev-digest-h...@river.apache.org wrote:

to be continued...



Reply via email to