Python's pickle.dump serializes a Python object into a byte stream you can write to disk, and the security catch is entirely on the other side: pickle.load will execute code embedded in that stream, so never unpickle data you did not create. The dump half is harmless. The danger lives in the round trip, and a huge number of Python codebases treat pickle files as if they were as inert as JSON. They are not.
Let me walk through what the function does, show a clean python pickle example, and then explain exactly why security teams flag it.
What pickle.dump Actually Does
pickle is Python's built-in object serialization module. pickle.dump(obj, file) writes a serialized representation of obj to an open binary file handle. The companion pickle.load(file) reads it back and reconstructs the object. There is also pickle.dumps and pickle.loads, which work with bytes in memory instead of a file.
Here is the canonical pickle.dump in python usage:
import pickle
data = {"user": "priya", "roles": ["admin", "auditor"], "active": True}
# Serialize to a file
with open("session.pkl", "wb") as f:
pickle.dump(data, f)
# Deserialize it back
with open("session.pkl", "rb") as f:
restored = pickle.load(f)
print(restored) # {'user': 'priya', 'roles': ['admin', 'auditor'], 'active': True}
Note the "wb" and "rb" modes. Pickle is a binary format, not text, so you always open the file in binary mode. That is the single most common beginner mistake, and it produces a TypeError rather than a silent corruption, so you will notice it fast.
A more complete python pickle dump example, serializing a custom object:
import pickle
class Report:
def __init__(self, title, findings):
self.title = title
self.findings = findings
r = Report("Q2 scan", ["CVE candidates: 3", "License flags: 1"])
with open("report.pkl", "wb") as f:
pickle.dump(r, f)
Pickle handles the class instance, its attributes, and nested structures without you writing any serialization logic. That convenience is exactly why it is everywhere: ML model checkpoints, cache layers, task queues like early Celery configurations, and inter-process message passing have all leaned on it.
The Part Nobody Reads: How Unpickling Runs Code
Pickle is not a data format in the way JSON is. It is a small stack-based virtual machine. A pickle stream is a program of opcodes, and one of those opcodes, REDUCE, tells the interpreter to call a callable with arguments. Objects can define __reduce__ to control how they are reconstructed, and that hook is what an attacker abuses.
Conceptually, a malicious object can define __reduce__ to return something like a call to os.system with a command string. When your code calls pickle.load on that stream, the "reconstruction" is the attack: the command runs. The Python documentation states this plainly, warning that the module is not secure and that you should never unpickle data received from an untrusted or unauthenticated source.
I am deliberately not printing a working payload here, because the point is defensive. The mechanism to internalize is simple: pickle.load on attacker-controlled bytes is equivalent to letting the attacker run Python in your process. It is a textbook insecure-deserialization flaw, the class of bug that OWASP has tracked for years and that shows up repeatedly in real CVEs across the Python ecosystem whenever a framework unpickles session cookies, cache entries, or uploaded files.
Where This Bites in Real Systems
The pattern that gets teams hurt is treating a pickle file as a trust boundary it is not. A few real shapes I have run into:
A web app stores session state as a pickled blob in a cookie or in a cache the user can influence. If the signing is weak or absent, the user hands you a pickle to load. Game over.
An ML pipeline downloads a .pkl or a legacy model checkpoint from a shared bucket or, worse, a public model hub, and loads it. If the storage or the supply chain is compromised, the model file is a code-execution vector. This is a live concern for anyone pulling pretrained artifacts, and it is part of why signed, verifiable model formats are gaining traction.
A background worker deserializes task payloads with pickle. Anyone who can enqueue a task can run code on the worker.
How to Use pickle Safely, or Avoid It
The first rule is a boundary rule: only ever pickle.load data your own trusted code produced and stored somewhere an attacker cannot tamper with. If the bytes crossed a trust boundary, do not unpickle them.
When you control both ends but the storage is shared, add integrity protection. Compute an HMAC over the serialized bytes with a secret key, store it alongside, and verify it before loading. That does not make pickle safe against a key compromise, but it stops opportunistic tampering.
Better still, prefer a data-only format when you are exchanging data rather than live objects. JSON, or msgpack for binary efficiency, cannot express "call this function," so they cannot carry a code-execution payload. For NumPy arrays, numpy.save with allow_pickle=False avoids the pickle path entirely. For configuration and structured records, this is almost always the right move.
If you must accept serialized objects from a less-trusted source and cannot change the format, run the deserialization in a locked-down sandbox with no network and minimal filesystem access, and assume it can be exploited.
Catching pickle Risk in Code Review and CI
Static analysis catches the obvious cases. Bandit, the Python security linter, flags pickle usage under checks B301 (pickle load) and B403 (pickle import) precisely so a reviewer has to justify each one. Wiring Bandit into CI turns "someone might unpickle untrusted data" into a build-time conversation.
Dependency scanning matters here too, because the risk is often not in your code but in a library that unpickles on your behalf. An SCA tool such as Safeguard can surface transitive dependencies with known deserialization CVEs so a vulnerable pickle path in a package three levels down does not stay invisible. If you want the broader picture on how deserialization sits among modern app-security risks, our security academy covers the injection and deserialization families together.
FAQ
Is pickle.dump itself dangerous?
No. Serializing your own objects with pickle.dump is safe. The risk is on deserialization: pickle.load and pickle.loads can execute code embedded in the stream, so the hazard appears only when you load data you did not produce.
What is a safe alternative to pickle in Python?
For plain data, use JSON (json module) or msgpack. Neither can represent executable behavior, so neither can carry a code-execution payload. For NumPy data, use numpy.save with allow_pickle=False.
Why does pickle require binary file mode?
Pickle produces a binary byte stream, not text, so files must be opened with "wb" for writing and "rb" for reading. Using text mode raises a TypeError on most platforms.
How do I detect unsafe pickle usage in my codebase?
Run Bandit, which flags pickle imports and loads via checks B403 and B301. Pair it with dependency scanning to catch libraries that unpickle untrusted input on your behalf.