FlawOpen
● CWE-502 / Unsafe Deserialization

Insecure Deserialization: Risks and Remediation

How untrusted pickle, Java serialization, and PHP unserialize data can trigger unsafe behavior—and how to replace or constrain it.

💡 Plain English Explainer (ELI5)

Serialization is like mailing a set of assembly instructions with a model. Deserialization follows those instructions to rebuild data. Some formats can also name classes or invoke hooks while rebuilding objects; accepting untrusted instructions can therefore trigger behavior the receiver did not intend. The exact impact depends on the format, runtime, reachable classes, and application context.

Core Concepts & Key Terms

Serialization / deserialization
Converting data or objects to a stored or transmitted representation, then parsing or reconstructing them in a program.
Deserialization hooks
Runtime callbacks such as Python __reduce__, Java readObject, or PHP __wakeup / __unserialize that may run as objects are reconstructed.
Gadget chain
A sequence of already-present library behaviors that can be composed to produce an unintended effect when deserialization reaches those classes. Availability is application- and version-specific.
Deserialization filter
A policy that constrains classes and/or graph metrics while reading an object stream. It reduces risk but does not make untrusted native object streams a preferred data format.

How Unsafe Deserialization Can Become an Exploit Chain

Step 1

Untrusted bytes reach an object decoder

A cookie, API body, queue message, or stored record contains data an attacker can influence.

Step 2

The application invokes a native object format

The code passes those bytes to a decoder such as Python pickle, Java ObjectInputStream, or PHP unserialize().

Step 3

Reconstruction invokes format-specific behavior

Depending on the format and reachable classes, constructors, hooks, autoloading, or library methods may run before application-level validation.

Step 4

Impact depends on reachable behavior

A usable gadget or hook may cause unauthorized effects; other inputs may only fail or consume resources. Code execution is not automatic for every format or application.

Python / Flask: Unsafe Object Loading vs. Primitive JSON Validation

VULNERABLE PATTERN
# VULNERABLE: pickle can execute behavior during unpickling.
from flask import Flask, request
import pickle

app = Flask(__name__)

@app.post("/api/session")
def load_session():
    # Never deserialize attacker-controlled bytes with pickle.
    session = pickle.loads(request.get_data())
    return {"user_id": session.user_id}
HARDENED DEFENSE
# SAFER: parse JSON primitives, then validate expected shape.
from flask import Flask, abort, request

app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024

@app.post("/api/session")
def load_session():
    payload = request.get_json()
    if not isinstance(payload, dict):
        abort(400, "Expected a JSON object")

    user_id = payload.get("user_id")
    if type(user_id) is not int or user_id <= 0:
        abort(400, "Invalid user_id")

    # Resolve authorization and session state server-side.
    return {"user_id": user_id}

Engineering Hardening Checklist

JSON still needs input-size limits and structural validation. For legacy object formats, do not rely on a class allowlist as the only boundary: authenticate the exact serialized bytes before deserialization and protect the keys. A MAC does not encrypt data or prevent replay.

← Browse Full Security Directory Explore Vulnerability Playbooks →