Java Serialization is often considered a dark art of Java programmers. This session will lift the veil and show what serialization is and isn't, how you can use it for profit and evil. After this session no NotSerializableException will be unconquerable.
8. Storage of objects
Copying data
Caching of data
HTTP sessions
Transmitting data/objects
across network
Why
Serialization?
9. Default Java Serialization
Custom Java Serialization
Versioning
Serialization in a nutshell
part 2
How Does Java
Serialization
Work?
part 2
Security
13. Java
Serialization
in a nutshell
class Foo implements Serializable {
}
Foo foo = new Foo();
FileOutputStream fos =
new FileOutputStream("foo.ser");
ObjectOutputStream oos =
new ObjectOutputStream(fos);
14. Java
Serialization
in a nutshell
class Foo implements Serializable {
}
Foo foo = new Foo();
FileOutputStream fos =
new FileOutputStream("foo.ser");
ObjectOutputStream oos =
new ObjectOutputStream(fos);
oos.write(foo);
17. Java
Serialization
in a nutshell
class Foo implements Serializable {
}
FileInputStream fis =
new FileInputStream("foo.ser");
ObjectInputStream ois =
new ObjectInputStream(fis);
18. Java
Serialization
in a nutshell
class Foo implements Serializable {
}
FileInputStream fis =
new FileInputStream("foo.ser");
ObjectInputStream ois =
new ObjectInputStream(fis);
Object object = ois.readObject();
19. Java
Serialization
in a nutshell
class Foo implements Serializable {
}
FileInputStream fis =
new FileInputStream("foo.ser");
ObjectInputStream ois =
new ObjectInputStream(fis);
Foo foo = (Foo) ois.readObject();
20. Default Java Serialization
Custom Java Serialization
Versioning
Serialization in a nutshell
part 2
How Does Java
Serialization
Work?
part 2
Security
21. Rules of Default Serialization
1. Implement java.io.Serializable
2. Identify (non-)serializable fields
3. Have access to no-args constructor
of first non-serializable superclass
class Foo implements Serializable {
private int count;
private String name;
private Thread thread;
}
22. class Foo implements Serializable {
int f;
}
class Bar extends Foo {
int b;
}
Bar bar1 = new Bar();
bar1.f = 123;
bar1.b = 456;
ObjectOutputStream oos = new ...
oos.write(bar1);
ObjectInputStream ois = new ...
Bar bar2 = (Bar) ois.readObject();
Which are true?
bar2.f == 0
bar2.f == 123
bar2.b == 0
bar2.b == 456
23. class Foo implements Serializable {
int f;
}
class Bar extends Foo {
int b;
}
Which are true?
bar2.f == 0
bar2.f == 123
bar2.b == 0
bar2.b == 456
Bar bar1 = new Bar();
bar1.f = 123;
bar1.b = 456;
ObjectOutputStream oos = new ...
oos.write(bar1);
ObjectInputStream ois = new ...
Bar bar2 = (Bar) ois.readObject();
24. class Foo {
int f;
}
class Bar extends Foo
implements Serializable {
int b;
}
Bar bar1 = new Bar();
bar1.f = 123;
bar1.b = 456;
ObjectOutputStream oos = new ...
oos.write(bar1);
ObjectInputStream ois = new ...
Bar bar2 = (Bar) ois.readObject();
Which are true?
bar2.f == 0
bar2.f == 123
bar2.b == 0
bar2.b == 456
25. class Foo {
int f;
}
class Bar extends Foo
implements Serializable {
int b;
}
Which are true?
bar2.f == 0
bar2.f == 123
bar2.b == 0
bar2.b == 456
Bar bar1 = new Bar();
bar1.f = 123;
bar1.b = 456;
ObjectOutputStream oos = new ...
oos.write(bar1);
ObjectInputStream ois = new ...
Bar bar2 = (Bar) ois.readObject();
26. Rules of Default Serialization
1. Implement java.io.Serializable
2. Identify (non-)serializable fields
3. Have access to no-args constructor
of first non-serializable superclass
class Foo implements Serializable {
private int count;
private String name;
private Thread thread;
}
27. 2. Identify (non-)serializable fields
• primitive fields
• String, Float, Double, ...
• anything implementing
Serializable or Externalizable
• static fields
• fields of enum types
• local (physical) resources
connections, threads, file handles
Serializable Not Serializable
28. 2. Identify (non-)serializable fields
class Foo implements Serializable {
private int count;
private String name;
private transient Thread thread;
}
Use transient keyword to mark
fields not-serializable
29. 2. Identify (non-)serializable fields
class Foo implements Serializable {
private transient int count = 1234;
private String name;
private transient Thread thread;
}
ObjectInputStream ois = ...
Foo foo = (Foo) ois.readObject();
assert foo.thread == null;
assert foo.count == 0;
Use transient keyword to mark
fields non-serializable
Upon de-serialization non-
serializable fields are given a
default value:
0, false, null
30. 2. Identify (non-)serializable fields
class UsingSerialPersistentFields
implements Serializable {
private int f = 123;
private int g = 456;
private static final
ObjectStreamField[]
serialPersistentFields = {
new ObjectStreamField(
"f", Integer.TYPE) };
}
Use serialPersistentFields to
mark fields that are to be
serialized
Overrides transient keyword
Must be private static final
31. Rules of Default Serialization
1. Implement java.io.Serializable
2. Identify (non-)serializable fields
3. Have access to no-args constructor
of first non-serializable superclass
class Foo {
Foo() {
}
}
class Bar extends Foo
implements Serializable {
}
👍
32. Rules of Default Serialization
1. Implement java.io.Serializable
2. Identify (non-)serializable fields
3. Have access to no-args constructor
of first non-serializable superclass
class Foo {
Foo(int f) {
}
}
class Bar extends Foo
implements Serializable {
}
🚫
33. 3. Have access to no-args constructor of
first non-serializable super class
class Bar1 {
Bar1(int b) { }
}
class Bar2 extends Bar1
implements Serializable {
Bar2() {
super(1);
}
}
Which are true?
Serialization of bar2 succeeds
Serialization of bar2 fails with
NotSerializableException
Deserialization of b2 succeeds
Deserialization of b2 fails with
InvalidClassException
Bar2 bar2 = new Bar2();
oos.writeObject(bar2);
Bar2 b2 = (Bar2) ois.readObject();
34. 3. Have access to no-args constructor of
first non-serializable super class
class Bar1 {
Bar1(int b) { }
}
class Bar2 extends Bar1
implements Serializable {
Bar2() {
super(1);
}
}
Which are true?
Serialization of bar2 succeeds
Serialization of bar2 fails with
NotSerializableException
Deserialization of b2 succeeds
Deserialization of b2 fails with
InvalidClassException
Bar2 bar2 = new Bar2();
oos.writeObject(bar2);
Bar2 b2 = (Bar2) ois.readObject();
35. Steps of Default Serialization
class Foo implements Serializable {
}
ObjectOutputStream::writeObject(Object o)
43. Steps of Default Deserialization
1. Object read = «newFoo»;
2. read.readObject()
3. result = read.readResolve()
4. result.validateObject()
5. return result
class Foo implements Serializable {
private void readObject(...) {}
private Object readResolve() {}
private void validateObject() {}
}
ObjectInputStream::readObject()
44. Default Java Serialization
Custom Java Serialization
Versioning
Serialization in a nutshell
part 2
How Does Java
Serialization
Work?
part 2
Security
45. Using writeReplace for Placeholders
class NotActuallySerializable implements Serializable {
private Object writeReplace() {
return new Placeholder(someValue);
}
public static NotActuallySerializable of(String value) {
return ...;
}
}
class Placeholder implements Serializable {
private String value;
private Object readResolve() {
return NotActuallySerializable.of(value);
}
}
46. Using readResolve for Singletons
final class Serialization {
public static final Serialization YAY = new JavaEE("Yay");
public static final Serialization NAY = new JavaEE("Nay");
private final String value;
private Serialization(String v) {
this.value = v;
}
private Object readResolve() {
if(value.equals("Yay"))
return YAY;
else
return NAY;
}
}
47. class Foo implements Serializable {
static final Foo foo = new Foo();
private Object writeReplace() {
return "Hello!";
}
private Object readResolve() {
return foo;
}
}
oos.writeObject(Foo.foo);
Foo f1 = (Foo) ois.readObject();
readResolve/writeReplace
Which is true?
f1.equals("Hello!")
f1 == Foo.foo
f1 != Foo.foo
Exception is thrown
48. class Foo implements Serializable {
static final Foo foo = new Foo();
private Object writeReplace() {
return "Hello!";
}
private Object readResolve() {
return foo;
}
}
oos.writeObject(Foo.foo);
Foo f1 = (Foo) ois.readObject();
readResolve/writeReplace
Which is true?
f1.equals("Hello!")
f1 == Foo.foo
f1 != Foo.foo
Exception is thrown
49. class Foo implements Serializable {
private Object readResolve() {
return "Hello!";
}
}
class Bar extends Foo {
}
oos.writeObject(new Bar());
Object o = ois.readObject();
readResolve/writeReplace
Which are true?
o.equals("Hello!")
o instanceof String
o instanceof Bar
Exception is thrown
50. class Foo implements Serializable {
private Object readResolve() {
return "Hello!";
}
}
class Bar extends Foo {
}
oos.writeObject(new Bar());
Object o = ois.readObject();
readResolve/writeReplace
Which are true?
o.equals("Hello!")
o instanceof String
o instanceof Bar
Exception is thrown
51. class CustomValues implements Serializable {
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
// write custom data
}
writeObject
53. Externalizable
public interface Externalizable
extends Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException;
}
Must implement java.io.Externalizable
Must have public no-args constructor
Implement both writeExternal() and readExternal()
54. ObjectInputValidation
public interface ObjectInputValidation {
public void validateObject() throws InvalidObjectException;
}
Allows the complete deserialized object graph to be validated
before returning
Should register with ObjectInputStream (in readObject):
ois.registerValidation(this, 0);
Performed after readResolve()
55. Default Java Serialization
Custom Java Serialization
Versioning
Serialization in a nutshell
part 2
How Does Java
Serialization
Work?
part 2
Security
56. class Foobar implements Serializable {
private static final long serialVersionUID = 1L;
}
It is strongly recommended that all serializable classes explicitly declare
serialVersionUID values, since the default serialVersionUID computation is
highly sensitive to class details that may vary depending on compiler implementations,
and can thus result in unexpected serialVersionUID conflicts during
deserialization, causing deserialization to fail.
Always provide serialVersionUID
57. It is strongly recommended that all serializable classes explicitly declare
serialVersionUID values, since the default serialVersionUID computation is
highly sensitive to class details that may vary depending on compiler implementations,
and can thus result in unexpected serialVersionUID conflicts during
deserialization, causing deserialization to fail.
Always provide serialVersionUID
class Foobar implements Serializable {
private static final long serialVersionUID = 1L;
}
required!!!
58. Deleting fields
Can't go from Serializable →
Externalizable
Move classes up/down hierarchy
Serializable field → Non-serializable
field (static/transient)
primitive field type change
Class → Enum or Enum → Class
Remove Serializable/Externalizable
Adding fields
Adding classes
Removing classes
Adding write/readObject
Adding Serializable
Changing access modifiers for fields
Non-Serializable field → serializable
field
Incompatible changes Compatible changes
Change serialVersionUID Don't Change serialVersionUID
59. Default Java Serialization
Custom Java Serialization
Versioning
Serialization in a nutshell
part 2
How Does Java
Serialization
Work?
part 2
Security
62. public class Main {
public static void main(String[] args) throws Exception {
File file = new File(args[0]);
try (
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);) {
while (ois.available() >= 0)
ois.readObject();
}
}
}
63. $ java -jar ysoserial.jar CommonsCollections1 "Calc.exe" > gadget.ser
public class Main {
public static void main(String[] args) throws Exception {
File file = new File("gadget.ser")
try (
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);) {
while (ois.available() >= 0)
ois.readObject();
}
}
}
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
java Main gadget.ser
69. ApplicationScoped
Spring beans
Singletons
Services
@ApplicationScoped
class FooService {
void foo() {}
}
class Bar implements Serializable {
@Inject
private FooService fooService;
void doSomething() {
fooService.foo();
}
}
• Serializes too much (possibly whole
service layer)
• Deserializes to non-managed
services
• Deserialization gives multiple
instances of one service
70. ApplicationScoped
Spring beans
Singletons
Services
@ApplicationScoped
class FooService {
void foo() {}
}
class Bar implements Serializable {
@Inject
private FooService fooService;
void doSomething() {
fooService.foo();
}
}
• Use a serializable proxy that looks
up service (CDI)
• Use readResolve/writeReplace for
custom serialization/deserialization
• CDI @Singleton injection *doesn't*
inject a serializable proxy, but the
instance directly
72. Inner/Nested classes
class FooService {
class Bar implements Serializable {}
public Bar getBar() {
return new Bar();
}
}
ObjectOutputStream oos = ...;
FooService service = ...;
Bar bar = service.getBar();
oos.writeObject(bar);
Which is true?
gives compilation error at
one of last two lines
bar gets serialized
Exception is thrown
73. Inner/Nested classes
class FooService {
class Bar implements Serializable {}
public Bar getBar() {
return new Bar();
}
}
ObjectOutputStream oos = ...;
FooService service = ...;
Bar bar = service.getBar();
oos.writeObject(bar);
Which is true?
gives compilation error at
one of last two lines
bar gets serialized
Exception is thrown
74. Inner/Nested classes
class FooService {
class Bar implements Serializable {}
public Bar getBar() {
return new Bar();
}
}
ObjectOutputStream oos = ...;
FooService service = ...;
Bar bar = service.getBar();
oos.writeObject(bar);
Not serializable
requires
a Foo
instance
75. Agenda
1. What is (Java) Serialization?
2. How does Java Serialization work?
3. Common Pitfalls of Serialization
4. Summary