David M. Lloyd
david.lloyd at redhat.com
Fri Feb 12 14:55:55 UTC 2016
Things are getting confused (for me anyway) so I'm going to try and
spell out each idea separately.
Given a class hierarchy H of:
C -> B -> A -> NonSerializableClass -> Object
where C, B, and A are serializable classes, and protection domain M
which is initiating deserialization, I think the following requirements
* When M deserializes an instance of A, both M and the protection domain
of A must be sufficient to allow the deserialization of A
* When M deserializes an instance of B, both M and the protection domain
of B must be sufficient to allow the deserialization of B
* When M deserializes an instance of C, both M and the protection domain
of C must be sufficient to allow the deserialization of C
* Extrapolate generally
The following requirements have definite pros and cons, which is what I
think we're touching on in this thread:
* When M deserializes an instance of B (or any subtype thereof), the
protection domain of A must also be sufficient to allow the
deserialization of B
* When M deserializes an instance of C (or any subtype thereof), the
protection domains of both A and B must also be sufficient to allow the
deserialization of C
* Extrapolate generally
Based on the above, the following requirements might or might not make
* When M deserializes an instance of A, the protection domains of
NonSerializableClass and also Object must also be sufficient to allow
the deserialization of A
* Likewise for B & C, extrapolate generally
Regardless of which is the best set of requirements, one of the major
complicating questions is, what is the ideal representation of the
permission(s) needed to allow the above? I can see several use cases:
1. Grant the ability for a serializable class S to deserialize itself
2. Grant the ability for M to deserialize a specific class (by name?)
3. Grant the ability to any (serializable?) class X to allow
deserialization of a specific subtype (by name?)
4. Grant the ability to any (serializable?) class X to allow
deserialization of any subtype (think java.lang.Object, or general (but
harmless) base classes like java.util.EventObject)
The big problem with permissions by name is that names are completely
relative. It is totally valid (and happens in the wild today) that a
serialization graph contains classes from a variety of class loaders
which are isolated from one another - in fact, it's even possible to
have more than one class with the same class name and the same
superclass in the same graph! Given this fact, a flat class namespace
*cannot* be assumed.
I think that the *only* possible remedy for this is that it should be up
to each class' class loader (and/or module) to determine what permission
is required in order to deserialize that class. In this way, the JDK
(9) can use a permission of the form class+module name for installed
module classes, Java EE systems could use permissions in the form of
application+module+jar+class name for deployment, OSGi systems could use
permissions including bundle identification information, etc.
To be secure, the default class loader implementation of this method
would have to produce a result which causes all checks to fail,
preventing deserialization in a security manager environment of classes
whose class loaders do not explicitly allow for it.
On 02/12/2016 12:30 AM, Peter Firmstone wrote:
> Thanks David,
> I'd originally considered something like that, but I later realised I'd
> need to grant DeserializationPermission to other domains I might not
> want allow objects to be deserialized with, simply because their domain
> is included in the call stack. It looked like it would widen the scope,
> which was contrary to the intended goal of minimising the attack
> surface. In the end I decided to limit the context to only the domains
> of classes involved in deserialization.
>> On 02/11/2016 03:52 AM, Peter Firmstone wrote:
>> >/ An example might be more useful.
>> />/ Traditionally, when the first non serializable superclass zero
>> />/ constructor is called, the domains of the subclasses aren't
>> present on
>> />/ the call stack. Any security checks performed in the constructor
>> of the
>> />/ superclass will not include the subclasses domains.
>> />/ In the proposed case, prior to construction, all domains in the
>> />/ heirarchy of the to be deserialized object via the local
>> />/ ObjectStreamClass, are added to an AccessControlContext, which is
>> />/ passed to the SecurityManager two argument permission check.
>> Sure, that makes sense; in fact this could be a very good
>> standalone/incremental enhancement.
>> >/ Now an attacker will not be able to construct this object unless all
>> />/ domains have DeserializationPermission.
>> />/ If we use the stack context, it won't contain the yet to be
>> />/ classes.
>> True; perhaps the ideal solution would use the initial context plus a
>> per-deserializing-class context, so that both the original caller and
>> the class being deserialized have permissions. This would have the
>> advantage of consistent behavior, and would also allow the PD of each
>> class to restrict whether it can be deserialized (which would apply
>> globally no matter who was doing the deserialization in the VM).
>> - DML
More information about the core-libs-dev