river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Re: Security
Date Mon, 09 Feb 2015 12:42:52 GMT
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-help@river.apache.org wrote:
>>>
>>> to be continued...
>>
>


Mime
View raw message