Why this matters

The bug. readObject doesn't read a User — it reads whatever class type the bytes specify, runs each class's readObject callbacks, then attempts the cast. Several common libraries on the classpath (Commons Collections, Spring, Groovy) ship 'gadget' classes whose deserialization side-effects can be chained into arbitrary code execution. The attacker reaches RCE before your code even sees the deserialized object.

The fix. Don't use Java native serialization for untrusted input. Switch to JSON (Jackson, Gson) with a fixed target type — the deserializer reads only declared fields and instantiates only the requested class. If you absolutely must keep Java serialization, use Apache Commons IO's ValidatingObjectInputStream with a strict allowlist.

Reality check. This vulnerability class is the reason every major Java pen-test report from 2016–2023 includes a deserialization finding. The Equifax breach was a Struts variant; the same root cause.

Review heuristic

Every endpoint that updates a user-owned object needs an explicit allowlist of fields it accepts. Object.assign and equivalents on raw request bodies are the smoking gun.

External reference: CWE-269: Improper Privilege Management.

CWE-502; OWASP A08:2021.