OWASP & Standards

Insecure Deserialization in Python: Why pickle.load() Can Run Arbitrary Code

pickle.load() doesn't just read data back into memory — for untrusted input, it can execute arbitrary Python as a side effect of loading it.

F Fora AI Team July 4, 2026 7 min read

Python's pickle module is convenient enough that it shows up in places it probably shouldn't — caching objects, passing data between processes, storing session data. The documentation is direct about the risk, but it's easy to miss if you're reaching for pickle as a quick way to serialize an object rather than reading through the security notes first.

Why this is different from a normal parsing bug

Formats like JSON describe data — strings, numbers, lists, objects — and a JSON parser can only ever produce those things. pickle describes how to reconstruct a Python object, which can include instructions to call arbitrary functions with arbitrary arguments during reconstruction. A crafted pickle payload can include a call to os.system() or subprocess.run() as part of "rebuilding" an object, and that call executes the moment pickle.load() processes it — before your code even gets to inspect the result.

# If `data` came from a request, a cookie, or any source you don't
# fully control, this line can execute attacker-chosen code
import pickle
obj = pickle.loads(data)

Where this sneaks into real applications

The obvious case is an API that accepts a pickled object directly. The less obvious cases are more common: a caching layer (Redis, Memcached) that pickles Python objects for storage, where the cache itself might be reachable or poisonable by another part of the system; or a session backend that pickles session data and stores it somewhere a user could potentially influence, like a signed cookie with a weak or leaked signing key.

The fix depends on what you actually need

If you're serializing plain data — dicts, lists, strings, numbers — json does the same job and can't execute code on load, full stop. If you need to serialize more complex Python objects and the data never crosses a trust boundary (it's generated and consumed entirely by your own backend, never touched by user input), pickle is fine. The moment the serialized data could have been touched, stored, or influenced by an external party, it needs to either move to a safe format or be cryptographically signed and verified before deserialization — and even then, signing only proves authenticity, not that the format itself is safe to process from a source you don't fully control.

Takeaway

pickle.load() should be treated the same way as eval(): fine for data you generated yourself, and a code-execution vulnerability the instant it touches anything from outside your own system.

← Back to all posts