Vulnerability library  /  Deserialization

Insecure deserialization

Deserialization

Insecure deserialization occurs when an application reconstructs native objects from attacker-controlled data. Depending on the runtime and available classes, this can change application state, bypass checks, or execute code.

Also known as
Unsafe deserialization · Untrusted object deserialization · Object injection
Typical severity · Medium to Critical
Last updated
On this page: What it is
  1. 1 · What it is
  2. 2 · Why it matters
  3. 3 · Examples
  4. 4 · How to fix it
  5. 5 · How to test for it
  6. References

What it is#

Serialization converts an in-memory object into bytes or text for storage or transport. Deserialization reverses that process. Native object formats often preserve class names, types, references, or methods—not just plain data.

The vulnerability appears when untrusted serialized content is accepted as an object graph. Constructors, property setters, callbacks, proxy behavior, or existing library classes may run during reconstruction. Even without code execution, an attacker may set fields that the normal application workflow would never allow.

Common risky formats include Java native serialization, .NET BinaryFormatter, Python pickle, PHP object serialization, Ruby Marshal, and YAML loaders that instantiate arbitrary types.

Why it matters#

  • Remote code execution through a compatible gadget chain already present in the application.
  • Authentication or authorization bypass by changing trusted object fields.
  • Data tampering, such as changing prices, roles, workflow states, or tenant identifiers.
  • Denial of service through deeply nested, cyclic, or oversized object graphs.
  • Unexpected network or file operations triggered during object construction.
Typical severity

Medium when only constrained state manipulation is possible. Critical when a reachable native deserializer and usable gadget chain permit code execution in the application process. Actual severity depends on context: who is affected, what data or actions are reachable, and existing controls.

Examples#

✕ Vulnerablejava
// Vulnerable: the stream chooses which classes are instantiated.
try (ObjectInputStream input = new ObjectInputStream(request.getInputStream())) {
    OrderRequest order = (OrderRequest) input.readObject();
    orderService.submit(order);
}
✓ Fixedjava
// Fixed: parse data into a narrow DTO and reject unexpected properties.
public record OrderRequest(String productId, int quantity) {}

ObjectMapper mapper = JsonMapper.builder()
    .disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
    .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    .build();

OrderRequest order = mapper.readValue(request.getInputStream(), OrderRequest.class);
if (order.quantity() < 1 || order.quantity() > 100) {
    throw new BadRequestException("Invalid quantity");
}
orderService.submit(order);

JSON is not automatically safe if polymorphic type metadata is enabled. The important control is that the server chooses a narrow data type and validates its fields.

How to fix it#

1. Do not deserialize native objects from untrusted sources. Replace native formats with simple JSON, Protocol Buffers, or another data-only format using an explicit schema.

2. Disable polymorphic type selection. Do not let input specify a class, assembly, module, or type discriminator unless it is mapped through a minimal server-owned allowlist.

3. Remove obsolete deserializers. Replace Java native serialization, .NET BinaryFormatter, Python pickle, PHP object serialization, unsafe YAML loaders, and similar APIs in all externally influenced paths.

4. Validate after parsing. Enforce field types, lengths, ranges, allowed state transitions, ownership, and authorization. A structurally valid object can still represent an invalid business action.

5. Treat signatures as defense in depth. An authenticated envelope can prevent tampering only if keys are protected and every producer is trusted. It does not make a dangerous deserializer safe when an authorized producer can be compromised.

6. Reduce gadget availability and privilege. Remove unused libraries, update dependencies, and run parsing in a low-privilege process with restricted network and file access.

7. Bound resource use. Limit input size, nesting, object count, collections, references, and parse time.

8. Migrate stored data deliberately. Existing cookies, queues, caches, or database blobs may contain legacy serialized objects. Version the new format and retire old readers after a controlled migration.

How to test for it#

  • Search code and dependencies for native deserialization APIs, polymorphic JSON settings, unsafe YAML constructors, and generic object-message consumers.
  • Trace every serialized value back to its producer. Cookies, hidden fields, queue messages, cache entries, and signed blobs can still be attacker influenced.
  • In a test environment, change ordinary data fields and type metadata in a sample created by the application. Confirm that unexpected classes and fields are rejected before business logic runs.
  • Submit malformed, deeply nested, cyclic, and oversized test objects within agreed limits and confirm graceful rejection.
  • Verify that application restarts, key rotation, and format migrations do not silently re-enable the legacy reader.
  • Use dependency analysis to identify known gadget-bearing libraries, but do not execute public gadget chains against production systems.
  • Add regression tests that assert only the intended DTO types can be constructed and unauthorized state transitions remain blocked.

References#

Permalinklibrary.infinity8labs.com/insecure-deserialization/JSON
Found this in Breachvane? Your finding links here for background. Evidence, affected assets and retest status are in your Breachvane ↗ (opens in a new tab) workspace. Need help fixing it? Talk to us.