Deserialize untrusted data in Scala by avoiding native Java object streams, using a plain data format such as JSON into explicit case classes, verifying a signature before parsing, and, where native serialization must stay, applying a class allowlist with size and depth limits.
Is Scala different from Java here?
Not at the layer that matters. Scala runs on the JVM and can use the same ObjectInputStream as any Java code. The OWASP guidance we read does not mention Scala by name, so what follows applies the Java guidance to Scala code. Check your libraries for anything Scala specific.
What is the core rule?
OWASP's Deserialization Cheat Sheet says to avoid native serialization for untrusted data and use a pure data format such as JSON or XML, ideally with data transfer objects. Doing so greatly reduces the risk.
In Scala that means decoding into a case class whose fields are plain types, with a library that does not let the input choose the target type. If the stream names the class to build, the attacker picks what gets instantiated.
A constructed example: a service accepts a session blob from a cookie and reads it with an object stream. The blob picks its own classes. An attacker who can craft a blob chooses any class on your classpath, which is why a large dependency tree makes this worse.
What if you cannot drop native serialization?
Three controls from OWASP apply.
First, integrity. Only deserialize data that carries an authenticated signature, and reject unsigned messages before any parsing. A signature check that happens after deserialization is too late.
Second, a class allowlist. In Java, configure serialization filters with an application specific allowlist and limits on graph depth, references, array lengths and stream bytes. OWASP also says to bound input size separately, because filters do not cover some encoded strings or primitives. Overriding resolveClass in a custom stream can act as an allowlist, but the page calls that an illustrative check and not a complete defense.
Third, limit blast radius. Mark sensitive fields transient so they do not leak or get overwritten. For domain objects that must never be deserialized, declare a final readObject that always throws.
OWASP notes that agents which harden every ObjectInputStream exist, but says global changes are only safe for denylisting known malicious types. A denylist is a stopgap; an allowlist is the goal.
How do you find the risky spots?
Search for ObjectInputStream, readObject, and any library that wraps it. Check message queues, caches, session stores and RPC layers, since native serialization often hides in infrastructure rather than your own code. Then list which of those inputs can be influenced from outside your trust boundary.
Also review your dependency list. Many deserialization incidents depend on classes that come in transitively, which is why a dependency inventory helps. Safeguard can generate an SBOM and scan the JVM dependencies of an sbt or Maven build for known issues, and reachability analysis can help you decide which ones your code actually calls.
What to do
- Inventory every place your Scala services read bytes into objects.
- Replace native serialization with JSON or another plain format decoding into explicit case classes.
- Make sure the decoder cannot choose the target type from input.
- Add authenticated signatures to any message that must keep a binary form, and verify before parsing.
- Where native streams stay, set a serialization filter with an allowlist, depth, reference, array and byte limits.
- Cap input size before it reaches the deserializer.
- Mark sensitive fields transient and block deserialization for classes that should never be built from input.
- Add a test that feeds an unexpected class and expects rejection.
Quick check
Is a case class safe to deserialize?
Not automatically. The type says nothing about trust. If the input decides which class is built, any serializable class on the classpath is a candidate.
Is a denylist of bad classes enough?
OWASP treats denylisting as acceptable only for broad hardening against known malicious types. An allowlist of the classes you expect is the stronger control.
Sources
- OWASP Deserialization Cheat Sheet (fetched 18 October 2026)
Last reviewed: 18 October 2026.