See: Description
Interface | Description |
---|---|
AtomicSerial.ReadObject |
ReadObject that can be used to read in data and Objects written
to the stream by writeObject() methods.
|
Class | Description |
---|---|
AtomicExternal.Factory |
Factory to test AtomicExternal instantiation compliance.
|
AtomicMarshalInputStream |
ObjectInputStream hardened against DOS attack.
|
AtomicMarshalledInstance |
Implementation of MarshalledInstance that performs input validation
during un-marshaling.
|
AtomicMarshalOutputStream |
This AtomicMarshalOutputStream, replaces a number of Java Object's in the stream
with Serializer's that ordinarily would not be deserializable by
AtomicMarshalInputStream or would not be safe to be deserialized, this
includes, but is not limited to Java Collections classes, Throwable
subclasses and object versions of primitive values.
|
AtomicSerial.Factory |
Factory to test AtomicSerial instantiation compliance.
|
AtomicSerial.GetArg |
GetArg is the single argument to AtomicSerial's constructor
|
DeSerializationPermission |
Permission that when granted, allows de-serialization of an object.
|
Valid |
Utilities for validating invariants.
|
Exception | Description |
---|---|
ArrayClassNotFoundException | |
AtomicException |
Although most Throwable classes are serialized over AtomicMarshalOutputStream,
only Throwable's fields are transferred.
|
CircularReferenceException | |
OptionalDataException |
Has the same semantics as
OptionalDataException , exists
in case we can't de-serialize OptionalDataException . |
Annotation Type | Description |
---|---|
AtomicExternal |
Classes annotated with this are expected to have a single argument public
constructor that accepts an ObjectInput instance and implement Externalizable.
|
AtomicSerial |
Traditional java de-serialization cannot be used over untrusted connections
for the following reasons:
The serial stream can be manipulated to allow the attacker to instantiate
any Serializable object available on the CLASSPATH or any object that
has a default constructor, such as ClassLoader.
|
AtomicSerial.ReadInput |
If an object wishes to read from the stream during construction
it must provide a class static method with the following annotation.
|
Serializer |
Used to annotate a class that contains a readResolve method with the
return type.
|
Atomic serialization is designed to allow users to perform de-serialization of untrusted data, serial form of @AtomicSerial objects is compatible with Java Serialization.
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import net.jini.core.lookup.ServiceMatches;
import org.apache.river.api.io.AtomicSerial;
import org.apache.river.api.io.AtomicSerial.GetArg;
import org.apache.river.api.io.Valid;
@AtomicSerial
class Matches implements Serializable {
private static final long serialVersionUID = 2L;
/**
* ServiceMatches.items as an List of Item
*
* @serial
*/
private final List<Item> items;
/**
* ServiceMatches.totalMatches
*
* @serial
*/
private final int totalMatches;
Matches(GetArg arg) throws IOException{
this(Valid.copyCol(arg.get("items", null, List.class),
new LinkedList<Item>(),
Item.class),
arg.get("totalMatches",0)
);
}
/** Simple constructor. */
Matches(List<Item> items, int totalMatches) {
this.items = items;
this.totalMatches = totalMatches;
}
/** Converts a Matches to a ServiceMatches. */
ServiceMatches get() throws RemoteException {
return new ServiceMatches(Item.toServiceItem(items), totalMatches);
}
}
Notice the annotation @AtomicSerial? this indicates the class has a constructor that accepts a single GetArg argument. Implementing @AtomicSerial is actually very simple, and in most cases only requires the addition of a constructor. Valid provides utility methods to simplify validation of invariants, in this case the List is being defensively copied into a new LinkedList and all elements are type checked, ensuring they are instances of Item. You may have also noticed that GetArg.get(String name, T default, Class type) performs type checking.
In case you're wondering why type checking is necessary, java Generic's only perform type checks at compile time, an attacker could send you a Collection containing an object you're not expecting, so it's important to validate the contents of your collections during construction.
It is a requirement of @AtomicSerial that all invariant checks are performed before calling a superclass constructor, this guarantees an instance of your object will not be created if invariants are not satisfied, thus an attacker cannot obtain an instance using a finalizer or stream reference. GetArg is caller sensitive, so you can pass it to a superclass constructor, the superclass and extending child class cannot get at each other's fields and they may even use the same field names without conflict, because each class has its own field namespace.
Note that the Item class above also implements @AtomicSerial, it too performs invariant validation. In fact, the administrator can use the java security policy to control which codebases may be involved in deserialization, by granting permission org.apache.river.api.io.DeSerializationPermission "ATOMIC"; Serializable Object's that only have primitive fields, or are stateless, don't need to implement @AtomicSerial and are not controlled by DeSerializationPermission at this time.
There's also a lot more going on under the hood, the developer need not be too concerned about, for example the above List of items is replaced with a safe List implementation during serialization by org.apache.river.api.io.AtomicMarshalOutputStream. Because Java Collections cannot be safely deserialized without the risk of DOS occurring, Collection, List, Set, SortedSet, Map and SortedMap's are all replaced in the stream, so most of the hard work is done for you, all you have to do is defensively copy during construction, perform type checks and check any other invariants you may have. Valid also provides a lot of other useful invariant validation methods a developer might typically perform, such as throwing an InvalidObjectException with a message if an argument shouldn't be null.
Unlike standard java serialization, the attacker doesn't have access to the full classpath, but a much reduced attack surface; only classes belonging to domains with DeSerializationPermission. Attackers are much less likely to be able to create a graph of objects with method call chains used for gadget attacks, provided scope has been limited by DeSerializationPermission and each object checks its own invariants.
Object's require permission to be created, while invariant checks and Java's strong type system limit the possible graph of object references an attacker can create. Circular references are prohibited to avoid infinite recursion and limits are placed on the serial Object cache and array lengths. Objects are responsible for recreating their own circular reference structures during construction if required.
Copyright © 2016–2018 The Apache Software Foundation. All rights reserved.