Skip to main content

Safe Deserialization

Source: Effective Java, 3rd Edition — Chapter 12 by Joshua Bloch (Items 88–90)

This lesson closes Chapter 12 — and the Effective Java intermediate track: defensive readObject, enum singletons over readResolve, and the serialization proxy pattern.

Item 88: Write readObject methods defensively​

readObject is a public constructor that takes a byte stream. Attackers can craft streams that violate invariants or steal references to internal mutable state (the Period/Date attacks).

Treat readObject like a constructor (Items 49–50):

  1. Defensive-copy mutable components (before validation)
  2. Validate invariants — throw InvalidObjectException on failure
  3. Never call overridable methods during deserialization

Litmus test: would you add a public constructor that assigns every nontransient field with no checks? If not, fix readObject — or use a serialization proxy (Item 90).

Quiz

Defensive readObject

1 / 2

Immutable serializable class with private mutable Date fields

Pick an answer to see if you are right.

Item 89: For instance control, prefer enum types to readResolve​

readResolve can swap the deserialized instance for a canonical one (e.g. singleton INSTANCE). It is fragile: nontransient reference fields can be “stolen” before readResolve runs (Elvis stealer attack).

Prefer a single-element enum for singletons (Item 3) — serialization guarantees constant identity without readResolve.

If instances aren’t known at compile time, readResolve may be necessary — make reference fields transient and use private on final classes.

Quiz

Instance control

1 / 2

Serializable singleton with readResolve and nontransient String[] field

Pick an answer to see if you are right.

Item 90: Consider serialization proxies instead of serialized instances​

The serialization proxy pattern:

  1. private static nested SerializationProxy copies logical state
  2. writeReplace() on the enclosing class returns the proxy
  3. readObject on the enclosing class throws InvalidObjectException (blocks fake instances)
  4. readResolve() on the proxy rebuilds via public API (constructors/factories)

Benefits: final fields stay final, invariants enforced by normal construction, attacks from Items 88–89 blocked. Cost: ~14% overhead vs defensive readObject in Bloch’s tests. Not for user-extendable classes or graphs with circularities during readResolve.

Items 88–90 map​

ItemMove
88readObject = constructor: copy, validate, no overridables
89enum singleton beats readResolve
90Proxy: writeReplace, reject readObject, proxy readResolve

Chapter 12 complete. Effective Java intermediate track complete (Items 1–90).