Explicit Serialization API and Security

Peter Levart peter.levart at gmail.com
Sat Jan 3 15:36:18 UTC 2015


On 01/03/2015 01:38 PM, Peter Firmstone wrote:
>
> > Hi,
> >
> > I would like to know what are the potential issues with simple
> > constructor chaining where each constructor checks the invariant of its
> > class state (with the already initialized state of superclass(es)).
>
> Finalizer attack; a subclass can override the finalize method and
> receive a thread of execution, even when it hasn't gotten hold of a 
> reference during construction. It's best to prevent an object's 
> construction, by throwing any exceptions before Object's default 
> constructor is called.
>

As Brian points out, this scheme can only validate intra-class 
invariants. It can't validate class-against-subclass state. And, the 
finalize() method will be called anyway - although this time with fully 
uninitialized instance (all fields default values). If this is safer, 
then alright.

Is there anything in JLS that guarantees finalization for instances 
which fail construction or deserialization? Wouldn't it be better to 
"register" for finalization just those instances that complete their 
construction or deserialization normally? I'm just trying to understand 
why it is the way it is.

Would something like this prevent Finalizer attacks?

- leave finalization registration the way it is (at object allocation time).
- provide internal API with which a previously registered object can be 
de-registered
- deserialization infrastructure de-registers the instances that fail 
deserialization


Regards, Peter

>
> A class can override the finalize method and make it final, but than 
> can cause objects to hang around longer than necessary.
>
> It's worth noting that all classes that implement Serializable have a 
> default no arg constructor, so presently don't have the option of 
> preventing object construction and typically have to clone mutable 
> referents after deserialization. Final fields only provide protection 
> for immutable referent objects.
>
> So this offers an improvement, at least for objects that don't have 
> circular links and it would allow them to retain compatibility with 
> their existing serial form.
>
> Cheers,
>
> Peter.
>
> For
> > example, the above would be written as:
> >
> > class A {
> >          final int lower, upper;
> >          A(ReadSerial rs) {
> >                  int l = rs.getInt("lower");
> >                  int u = rs.getInt("upper");
> >                  if (l > u) throw new IllegalArgumentException();
> >                  lower = l;
> >                  upper = u;
> >          }
> > }
> >
> > class B extends A {
> >          final int cur;
> >          B(ReadSerial rs) {
> >                  super(rs);
> >                  int c = rs.getInt("cur");
> >                  if (c < lower || c > upper) throw new
> > IllegalArgumentException();                cur = c;
> >          }
> > }
> >
> >
> > >
> > > Another challenge in invariant checking is circular data structures.
> > > If you have two objects:
> > >
> > > class Brother {
> > > final Brother brother;
> > > }
> > >
> > > that refer to each other, an invariant you might want to check after
> > > deserialization is that
> > > this.brother.brother == this
> >
> > This is tricky, yes. In general, the graph of objects has to be linked
> > somehow gradually where links are established to half-initialized
> > objects. So there would have to be a special post-deserialization
> > call-back over the deserialized objects just for checking the
> > invariants. But what to do with the instances that "escape" during the
> > process? This is why explicit (de)serialization is tricky - it allows
> > arbitrary user code to be executed during the construction of the 
> object
> > graph.
> >
> > Perhaps we just need some kind of declarative prescription of 
> serialized
> > state and mapping to fields of objects in the form of annotations or
> > such (see what JPA is doing in this field). Combined with
> > post-deserialization invariant-checking call-back method perhaps.
> >
> > Regards, Peter
> >
> > >
> > > Obviously you have to patch one or the other instance after
> > > construction to retain the circular references; at what point do you
> > > do invariant checking?
> > >
> > > On 1/1/2015 7:43 AM, Peter Firmstone wrote:
> > > > Subclass example:
> > > >
> > > > class SubFoo extends BaseFoo {
> > > >
> > > > public static ReadSerial check(ReadSerial rs){
> > > > if (rs.getInt("y") > 128) throw Exception("too big");
> > > > return rs;
> > > > }
> > > >
> > > > private final int y;
> > > >
> > > > public SubFoo( int x , int y) {
> > > > super(x);
> > > > this.y = y;
> > > > }
> > > >
> > > > public SubFoo( ReadSerial rs ){
> > > > super(BaseFoo.check(check(rs)));
> > > > // SubFoo can't get at BaseFoo's rs.getInt("x"),
> > > > // it's visible only to BaseFoo. Instead SubFoo would get
> > > > // the default int value of 0. Just in case both classes have
> > > > // private fields named "x".
> > > > // ReadSerial is caller sensitive.
> > > > this.y = rs.getInt("y");
> > > > }
> > > > }
> > > >
> > > > Classes in the heirarchy can provide a static method that throws an
> > > > exception to check invarients while preventing a finaliser attack.
> > > > We'd want to check invarients for other constructors also, but for
> > > > berevity...
> > > >
> > > > Eg:
> > > >
> > > > class BaseFoo implements Serializable{
> > > >
> > > > public static ReadSerial check(ReadSerial rs) throws Exception
> > > > {
> > > > if (rs.getInt("x") < 1)
> > > > throw IllegalArgumentException("message");
> > > > return rs;
> > > > }
> > > > ....
> > > >
> > > >
> > > > Sent from my Nokia N900.
> > > >
> > > > ----- Original message -----
> > > > So, if I understand this correctly, the way this would get used is:
> > > >
> > > > class BaseFoo implements Serializable {
> > > > private final int x;
> > > >
> > > > public BaseFoo(ReadSerial rs) {
> > > > this(rs.getInt("x"));
> > > > }
> > > >
> > > > public BaseFoo(int x) {
> > > > this.x = x;
> > > > }
> > > > }
> > > >
> > > > Right?
> > > >
> > > > What happens with subclasses?  I think then I need an extra RS arg
> > > > in my
> > > > constructors:
> > > >
> > > > class SubFoo extends BaseFoo {
> > > > private final int y;
> > > >
> > > > public SubFoo(ReadSerial rs) {
> > > > this(rs.getInt("y"));
> > > > }
> > > >
> > > > public BaseFoo(ReadSerial rs, int y) {
> > > > super(rs);
> > > > this.y = y;
> > > > }
> > > > }
> > > >
> > > > Is this what you envision?
> > > >
> > > >
> > > >
> > > >
> > > >
> > > > On 12/27/2014 8:03 PM, Peter Firmstone wrote:
> > > > Is there any interest in developing an explicit API for
> > > > Serialization?:
> > > >
> > > > 1. Use a public constructor signature with a single argument,
> > > > ReadSerialParameters (read only, writable only by the
> > > > serialization framework) to recreate objects, subclasses (when
> > > > permitted) call this first from their own constructor, they have
> > > > an identical constructor signature. ReadSerialParameters that are
> > > > null may contain a circular reference and will be available after
> > > > construction, see #3 below.
> > > > 2. Use a factory method (defined by an interface) with one
> > > > parameter,
> > > > WriteSerialParameters (write only, readable only by the
> > > > serialization framework), this method can be overridden by
> > > > subclasses (when permitted)
> > > > 3. For circular links, a public method (defined by an interface)
> > > > that
> > > > accepts one argument, ReadSerialParameters, this method is called
> > > > after the constructor completes, subclasses overriding this should
> > > > call the superclass method.  If this method is not called, an
> > > > implementation, if known to possibly contain circular links,
> > > > should check it has been fully initialized in each object method
> > > > called.
> > > > 4. Retains compatibility with current serialization stream format.
> > > > 5. Each serial field has a name, calling class and object
> > > > reference,
> > > > similar to explicitly declaring "private static final
> > > > ObjectStreamField[] serialPersistentFields ".
> > > >
> > > > Benefits:
> > > >
> > > > 1. An object's internal form is not publicised.
> > > > 2. Each class in an object's heirarchy can use a static method to
> > > > check invarients and throw an exception, prior to
> > > > java.lang.Object's constructor being called, preventing
> > > > construction and avoiding finalizer attacks.
> > > > 3. Final field friendly.
> > > > 4. Compatible with existing serial form.
> > > > 5. Flexible serial form evolution.
> > > > 6. All methods are public and explicitly defined.
> > > > 7. All class ProtectionDomain's exist in the current execution
> > > > context, allowing an object to throw a SecurityException before
> > > > construction.
> > > > 8. Less susceptible to deserialization attacks.
> > > >
> > > > Problems:
> > > >
> > > > 1. Implementations cannot be package private or private.  Implicit
> > > > serialization publicises internal form, any thoughts?
> > > >
> > > > Recommendations:
> > > >
> > > > 1. Create a security check in the serialization framework for
> > > > implicit serialization, allowing administrators to reduce their
> > > > deserialization attack surface.
> > > > 2. For improved security, disallow classes implementing explicit
> > > > serialization from having static state and static initializer
> > > > blocks, only allow static methods, this would require complier and
> > > > verifier changes.
> > > > 3. Alternative to #2, allow final static fields, but don't allow
> > > > static initializer blocks or mutable static fields, similar to
> > > > interfaces.
> > > >
> > > > Penny for your thoughts?
> > > >
> > > > Regards,
> > > >
> > > > Peter Firmstone.
> > > >
> >
>




More information about the core-libs-dev mailing list