Best option: do not deserialize untrusted Java objects at all. If you cannot avoid it, configure a serialization filter with an application specific class allowlist and resource limits before reading any object. OWASP is blunt that filtering does not make arbitrary untrusted deserialization safe.
Why Java deserialization is dangerous
When Java reads a serialized stream, it creates objects and runs their special methods such as readObject. If an attacker can choose which classes appear in the stream, they can chain together existing classes on your classpath to do things you never intended. New chains keep being found, so lists of known bad classes are always incomplete.
OWASP lists signs that Java serialization is in use: the bytes AC ED 00 05, the Base64 prefix rO0, and the content type application/x-java-serialized-object. Spotting those in traffic or storage tells you where to look.
Step 1: remove the need
OWASP's general advice is to prefer pure data formats such as JSON or XML with data transfer objects. A deserializer can only instantiate types that it knows about, so make the type fixed by your code, not chosen by the data.
Step 2: filter when you cannot
OWASP says to configure serialization filters with an allowlist of expected classes and limits on graph depth, number of references, array length and stream size. Limits matter because class checks alone do not bound resource use.
Two details from the cheat sheet:
- Filters are not called for concretely encoded strings or primitives, so cap the input size separately.
- Overriding
resolveClass()to permit one expected class is an illustration, not a full defense. It does not check every object, and dynamic proxy descriptors go throughresolveProxyClass().
An allowlist is stronger than a blocklist because you list what the application needs, not what attackers have already used.
Step 3: harden the domain objects
- Make a
final readObject()that always throws on classes that must not be deserialized. - Mark sensitive fields
private transientso they are not serialized or controllable through deserialization. - Review sinks:
readObject,readResolve,readExternal,readUnshared,XMLDecoderandXStream.fromXML.
Libraries that matter
OWASP's notes on other Java libraries are worth checking against your dependency list. Per its page, these are safe with default configuration: fastjson2, Jackson without polymorphism, Kryo 5.0.0 and later, YamlBeans 1.16 and later, and XStream 1.4.17 and later. Others need configuration, such as SnakeYAML with SafeConstructor, or should not be used with untrusted input, such as the JDK XMLDecoder. Check the current page for the full list, since it changes.
When you do not own the code
If a third party library deserializes and you cannot patch it quickly, OWASP mentions agents that instrument all ObjectInputStream use. It warns a global change is only safe for blocking known malicious types.
A short plan
- Inventory where
ObjectInputStreamand similar sinks appear. - Replace the ones on public paths with JSON and explicit types.
- Put filters in front of the rest.
- Keep libraries current and scan for known gadget bearing versions.
Safeguard's SBOM and dependency scanning and reachability analysis help with step 4. A free scan is at gold.safeguard.sh/scan. For other languages see serialization risk across Python, Java and .NET.
Quick check
Is a class allowlist enough?
It is the right first control, but OWASP says filtering does not make untrusted deserialization safe. Add size limits and avoid native serialization where possible.
How do I tell if my service uses Java serialization?
Search for ObjectInputStream, and look for the byte prefix AC ED 00 05 or Base64 rO0 in traffic and stored data.
Sources
Last reviewed: 19 October 2026.