FlawOpen

flawopen.com/Insecure Deserialization/Python pickle security

Is Python pickle.load() safe for untrusted data?

CWE-502: Deserialization of Untrusted DataReference page
Short answer

No. Treat pickle like a program sent inside a parcel, not like a passive data record: loading it can invoke reconstruction behavior before your application validates the resulting object. Use a data-only format for inputs that cross a trust boundary.

UNSAFE — untrusted bytes can trigger code during loading
import pickle

# Never accept pickle bytes from a request, upload,
# shared cache, or other untrusted source.
obj = pickle.loads(untrusted_bytes)
SAFER DEFAULT — parse data, then validate its schema
import json

def parse_profile(raw: bytes) -> dict[str, str]:
    if len(raw) > 65_536:
        raise ValueError("Payload too large")

    try:
        value = json.loads(raw)
    except (json.JSONDecodeError, UnicodeDecodeError):
        raise ValueError("Invalid JSON") from None

    if not isinstance(value, dict) or set(value) - {"display_name", "theme"}:
        raise ValueError("Invalid profile")

    name = value.get("display_name")
    theme = value.get("theme")
    if not isinstance(name, str) or len(name) > 80:
        raise ValueError("Invalid display name")
    if theme not in {"light", "dark"}:
        raise ValueError("Invalid theme")

    return {"display_name": name, "theme": theme}
Pickle stream:
A Python-specific byte sequence that describes object reconstruction, including references to globals and reconstruction behavior.
Unpickling:
Interpreting a stream and rebuilding its object graph; reconstruction may invoke callable behavior.
Trust boundary:
Any point where bytes can be controlled or modified by a user, another service, an upload, or a shared store.
CWE-502:
Deserialization of untrusted data; impact depends on whether an attacker can supply or tamper with data that the application loads.
Step 1

Untrusted bytes reach a pickle sink

An upload, request, cache entry, or artifact carries attacker-controlled or tampered pickle data into the process.

Step 2

The unpickler resolves globals

The interpreter processes reconstruction instructions; find_class() can import and return named globals, including functions.

Step 3

Loading triggers behavior

Instructions can invoke callables while load() or loads() runs, before application checks on the returned object.

Step 4

Impact follows process authority

Behavior runs with the Python process permissions. Network reach, secrets, filesystem access, and sandboxing determine the blast radius.

Why pickle loading crosses a code-execution boundary

A pickle stream can name globals and reconstruction callables. During unpickling, Python’s Unpickler.find_class() resolves globals, including functions; opcodes can then invoke reconstruction behavior. The dangerous step is pickle.load() or pickle.loads() itself—there is no later, separate execute call. The process runs resulting code with its operating-system permissions. This is the format’s documented behavior, not a parser bug; do not use pickle as an interchange format for untrusted input.

What JSON fixes—and what it does not

Python documents that unpickling untrusted or tampered data can execute arbitrary code, while JSON decoding does not itself create that arbitrary-code-execution path. JSON is not automatic input validation: cap payload size, require the expected top-level type and fields, validate each value against a schema, and apply authorization separately. If a legacy workflow must load pickle, authenticate the exact bytes with a protected key before unpickling; a signature proves origin and integrity only under its key assumptions and does not make malicious data from a trusted signer safe. Prefer migration to a data-only format.

Frequently asked questions

Can a restricted Unpickler make attacker-controlled pickle safe?

Overriding find_class() can reduce risk by restricting globals, but proving a restricted loader safe across protocols and reachable behavior is difficult. Do not treat an allow-list as the default defense for user-controlled bytes; prefer a data-only format.

Does JSON need validation too?

Yes. JSON avoids pickle-style arbitrary code execution during decoding, but applications must still validate type, fields, sizes, value ranges, and authorization. Treat JSON parsing and business-rule validation as separate steps.

Primary references

See also Insecure Deserialization: Risks and Remediation Is Ruby YAML.load Safe? Psych Versions & Safe Loading .NET BinaryFormatter Security: Risks and Migration Java ObjectInputStream Security: Safer Deserialization Newtonsoft.Json TypeNameHandling: Safe Configuration? PHP unserialize() and Untrusted Input