Isn't the issue with non-serializable superclass constructor call this one? : http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5353
If so - I don't really see how it relates to River - to be able to expoit this kind of vulnerability an attacker must have already downloaded and run his code - otherwise the exploiting subclass could not have been loaded hence no construction would take place. I think if we can make sure only trusted (whatever that means :) ) code is ever run - we don't have to do anything more with (de)serialization - except making DoS more difficult by restricting size of the stream. Thanks, Michal Peter Firmstone wrote: > Continuing on ... > > Lets say for example, we have a secure OS and we provide a service on > a public port and we have a determined attacker attempting to use > deserialization to take over our system or bring it to its knees with > denial of service. > > We know this is relatively easy with standard ObjectInputStream. > > When we invoke a remote method, the return value is what you call a > root object, that is it's the root of a serialized object tree, > originating through the root object, via its fields. > > Most Serializable objects have invariants. > > We can consider the java serialization stream format as a string of > commands that allows creation of any object, bypassing all levels of > visibility. That is an attacker can create package private classes or > private internal classes, provided they implement Serializable. So > think of Serializable as a public constructor that lacks a context > representing the attacker. That's right, there is no context > representing the remote endpoint in the thread call stack. > > Now we can place restrictions on the stream, such as a requirement > that the stream is reset before a limit on the number of objects > deserialized is reached. We can also place a limit on the count of > all array elements read in from the stream, until the stream is > reset. These measures ensure the stream cannot go on forever, they > also prevent stack overflow and out of memory errors. At some point > the caller will regain control of the stream without running out of > resources. > > But getting back to the previous problem, the attacker has command > line access to create any Serializable object. > > Now most objects have invariants that should be satisfied, for correct > functional state, but a major problem with Serialization is it creates > an instance, using the first zero arg constructor of the first non > serializable superclass. Yep that's right, that could be ClassLoader, > you clever attacker you! The poor old object hasn't even had a chance > to check it's invariants and it's game over, that's not fair! > > This will never do, I had to create a new contract for atomic object > construction: > > 1. The class must implement Serializable (can be inherited) AND one > of the following conditions must be met: > 2. The class must be annotated with @AtomicSerial OR > 3. The class must be stateless and can be created by calling > class.newInstance() OR > 4. The class must have DeSerializationPermission > > Now if the class is annotated with @AtomicSerial, it must also have a > constructor signature that has public or default visibility: > > SomeClass(GetArg arg) throws IOException{ > // The first thing I must do is to call a static method that > checks invariants, before calling a superclass constructor, the static > method should return the argument for another constructor. > } > > Some simple rules for our object input stream: > > 1. Though shalt not publish a reference to a partially constructed > object. > 2. Though shalt not publish a reference if an object fails > construction, where readObject, readObjectNoData and readResolve > methods are considered to be constructors for the purpose of > deserialization of conventional Serializable objects. > 3. Though shalt not attempt to construct a Serializable object that > doesn't have an @AtomicSerial annotation, or has serial fields > (state) and doesn't have DeSerializationPermission. > 4. Only call a protected constructor if the class doesn't implement > @AtomicSerial and has DeSerializationPermission. > 5. Do not honour the standard java Serialization construction > contract (not all Serializable classes can be constructed even if > they have DeSerializationPermission). > 6. If a standard Serializable class has DeSerializationPermission for > it to be constructed it must have a zero arg constructor or a > constructor that accepts null object arguments and default > primitive values. > 7. If an any object in a serialized object graph fails it's invariant > checks, deserialization of the object graph fails at that point > and control is returned to the caller, by way of an > InvalidObjectException (a child class of IOException). > 8. Honour the standard java serialization stream protocol. > 9. Count number of objects received and throw a > StreamCorruptedException if limit is exceeded. > 10. Count number of array elements received and throw > StreamCorruptedException if limit is exceeded. > 11. readResolve() can be called on @AtomicSerial instances but, > readObject() is never called and neither is readObjectNoData(). > > Obligations of our object output stream: > > 1. Reset the stream before the limit is reached. > 2. Replace java collections and maps with safe @AtomicSerial > implementations, it is the developers obligation to replace them > with their preferred implementations during construction, these > are functional but are only immutable containers for keys, values > and comparators. > 3. Honour java's serial stream protocol. > > Some simple rules for developers implementing @AtomicSerial: > > 1. Check all invariants from a static method called by your class > constructor prior to calling a superclass constructor. > 2. Check types of values in collections and maps and keys in maps. > 3. Check field types. > 4. Copy or clone arrays and collections. > 5. Do not call a super class constructor until all invariants have > been checked. > 6. Handle failed invariants by throwing an InvalidObjectException, > again make sure you did not call the super class constructor. > 7. Implement the interface ReadObject if you want to duplicate > existing readObject method functionality, this will allow you to > communicate with the stream prior to calling a super class > constructor, so you can check invariants. Annotate a static > method that returns an instance of ReadObject with @ReadInput. > 8. Your ReadObject instance can be retrieved by from GetArg, your > ReadObject instance will have read the stream prior to your > constructor having been called. > 9. Don't call defaultReadObject() from your ReadObject implementation! > 10. Check all fields can be retrieved, prior to calling a super class > constructor. > 11. Beware of the implicit super class constructor. > 12. Enum, and Proxy instances are considered secure, don't bother > subclassing proxy, your class won't be deserialized. > 13. InvocationHandler's need to implement @AtomicSerial. > > Note an object instance is not created until the default constructor > in java.lang.Object is called. > > Golden rules: > > 1. Do not create arrays or collections blindly by reading in an > integer or long, length or size from the stream to pass as an > argument to an array or collection constructor. > 2. Let the stream be responsible for array creation. > 3. Clone or copy arrays and collections. > 4. Clone or copy mutable objects, these may be shared by other > objects in the stream. > 5. Don't grant DeSerializationPermission unless you're really really > sure you know what your doing, you must have access to the source > code of the object you want to deserialize, you must make sure it > is secure, if in doubt ask first. > > Now before anyone goes off thinking "Oh no I don't need this > functionality, do I have to use it?" The answer is no, it will be set > via configuration constraints. If an endpoint doesn't support > AtomicValidation, it will prevent ObjectInputStream creation. If an > object in the stream doesn't support it, deserialization will fail and > control will be returned to the caller. > > Other than configuration, this is invisible to clients, only service > api parameters and return values and private service classes, such as > smart proxy's and proxy verifiers need implement it. > > Note: I have tested both Reggie and Outrigger, standard serial form is > preserved for all objects, all tests pass with AtomicValidation.YES > enabled. > > Clients will be able to be configured with InvocationConstraints; > AtomicValidation.YES, Integrity.YES and Confidentiality.YES. > > The client can also require DownloadPermission by specifying either of > the properties: > > net.jini.loader.ClassLoading.provider=net.jini.loader.pref.RequireDlPermProvider > > > OR > > java.rmi.server.RMIClassLoaderSpi=net.jini.loader.pref.RequireDlPermProvider > > > If the first property is specified, it isn't required to be loaded by > the system ClassLoader. > > The next step is to provide a service interface for a bootstrap proxy > to return a smart proxy and to define a suitable Entry for the > bootstrap proxy to declare the interfaces it's smart proxy implements. > > Clients can then lookup bootstrap proxy, based on the Entry, > authenticate the bootstrap proxy and grant it DownloadPermission > dynamically. The client will then need to prepare the smart proxy, > placing constraints on it. > > While the bootstrap proxy should have an AtomicVerification.YES > constraint, the endpoint for the smart proxy doesn't have to, so after > trust has been established, it is possible to run with standard > serialization. In this case the smart proxy itself must be > serializable using @AtomicSerial, but objects serialized over the > smart proxy's endpoint's need not. I've also have made it possible > for proxy codebases to declare the permissions they require so these > can be granted dynamically after trust is established. > > This would also provide the client with delayed unmarshalling > functionality, while preserving the standard ServiceRegistrar > interface, which can be managed using ServiceDiscoveryManager, and > ServiceItemFilter. > > In other words, this additional functionality requires minimal effort > on behalf of the developer, while those who don't need it can remain > blissfully ignorant and implement it later if they want to. > > I haven't uploaded to svn, would you like to see some patches? > > Regards, > > Peter.