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.