What Is a .PKL File?
Python pickle
Python Pickle File (.pkl, .pickle)
Overview
Pickle is Python's native object serialisation format. It converts almost any Python object - nested dictionaries, class instances, NumPy arrays, trained models, functions by reference - into a byte stream that can be written to disk and reconstructed later as an equivalent object.
Its convenience is also its danger. Unpickling is not parsing: it is a small stack-based virtual machine that executes opcodes, and those opcodes can import modules and call functions. Loading a pickle file from an untrusted source is equivalent to running arbitrary code. This is the single most important thing to know about the format.
Technical Specifications
Format Details
- MIME Type:
application/octet-stream - File Extensions:
.pkl,.pickle,.p, sometimes.savor.joblib - Category: Data
- Structure: a stack-based opcode stream
- Protocol versions: 0 through 5
- Module:
picklein the Python standard library
Protocol versions
| Protocol | Introduced | Notes |
|---|---|---|
| 0 | Original | ASCII, human-readable, very verbose |
| 1 | Original | Binary form of protocol 0 |
| 2 | Python 2.3 | Efficient new-style class support |
| 3 | Python 3.0 | bytes support; not readable by Python 2 |
| 4 | Python 3.4 | Large objects, more compact; default since 3.8 |
| 5 | Python 3.8 | Out-of-band data buffers for zero-copy |
Identification
Protocol 2 and later begin with the PROTO opcode:
80 04 → protocol 4
80 05 → protocol 5
Protocol 0 is ASCII and looks like a sequence of short commands. Every pickle ends with the . opcode (0x2E), the STOP instruction.
Because there is no strong magic number, a .pkl file is best identified by disassembling the opcode stream rather than by signature matching alone.
How Pickle Works
The format is a program for a tiny stack machine. pickletools.dis shows this clearly:
import pickle, pickletools
data = {"name": "Ada", "scores": [1, 2, 3]}
blob = pickle.dumps(data, protocol=4)
pickletools.dis(blob)
Output (abbreviated):
0: \x80 PROTO 4
2: \x95 FRAME 54
11: } EMPTY_DICT
12: \x94 MEMOIZE
13: ( MARK
14: \x8c SHORT_BINUNICODE 'name'
22: \x94 MEMOIZE
23: \x8c SHORT_BINUNICODE 'Ada'
...
52: u SETITEMS (MARK at 13)
53: . STOP
The opcodes push values, build containers, and - critically - GLOBAL and REDUCE can import a module and call a callable. That is the mechanism the format needs to reconstruct class instances, and also the mechanism an attacker exploits.
Security: the central concern
A malicious pickle does not need to be complicated:
# Illustrative only - this is what a hostile payload looks like
class Exploit:
def __reduce__(self):
import os
return (os.system, ("whoami",))
Pickling an Exploit instance produces a file that runs whoami the moment anyone calls pickle.load on it. No warning, no sandbox, no opt-in.
Practical guidance
- Never unpickle data from an untrusted or unauthenticated source. There is no safe subset and no validating parser that makes this safe.
- Do not accept pickles over a network boundary, including model files downloaded from public model hubs. This is a live supply-chain risk: several model registries scan uploads specifically for hostile pickles.
- Prefer safer formats for interchange: JSON for plain data, Parquet for tables, NPY/NPZ for arrays, ONNX or safetensors for models.
- If you must accept pickles, sign them and verify the signature before loading, or restrict unpickling with a custom
Unpicklerthat overridesfind_classto an explicit allowlist:
import pickle, io
ALLOWED = {("collections", "OrderedDict"), ("builtins", "dict")}
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if (module, name) not in ALLOWED:
raise pickle.UnpicklingError(f"blocked: {module}.{name}")
return super().find_class(module, name)
def safe_loads(data: bytes):
return RestrictedUnpickler(io.BytesIO(data)).load()
Even this is a mitigation, not a guarantee. Treat it as defence in depth rather than a licence to load hostile input.
Common Use Cases
- Caching computed results: memoising expensive work between runs.
- Machine learning models: scikit-learn's documented persistence path, and the basis of
joblib. - Inter-process communication:
multiprocessingpickles objects to move them between workers. - Session and application state: saving a program's state to resume later.
- Celery and task queues: pickle was historically a default serialiser (now discouraged in favour of JSON).
How to Open a Pickle File
Loading
import pickle
with open("model.pkl", "rb") as f:
obj = pickle.load(f)
print(type(obj))
Saving
import pickle
with open("model.pkl", "wb") as f:
pickle.dump(obj, f, protocol=pickle.HIGHEST_PROTOCOL)
Inspecting without executing
This is the right first step for any file you did not create:
import pickletools
with open("unknown.pkl", "rb") as f:
pickletools.dis(f) # disassembles; does not execute
Look for GLOBAL or STACK_GLOBAL opcodes referencing os, subprocess, builtins.eval, or builtins.exec. Their presence in what should be a plain data file is a strong signal of tampering.
# From the shell
python -m pickletools unknown.pkl | head -40
# Quick check for suspicious references without running anything
strings unknown.pkl | grep -Ei 'os|subprocess|eval|exec|system'
With pandas and scikit-learn
import pandas as pd
df = pd.read_pickle("frame.pkl")
df.to_pickle("frame.pkl")
import joblib
joblib.dump(model, "model.joblib") # more efficient for large NumPy arrays
model = joblib.load("model.joblib") # same security caveats apply
Advantages
- Handles almost any Python object: including nested and custom types.
- No schema required: nothing to define in advance.
- Preserves object identity: shared and cyclic references are reconstructed correctly.
- Fast: binary protocols 4 and 5 are efficient.
- Standard library: no dependency.
Limitations
- Fundamentally unsafe to load from untrusted input: the defining drawback.
- Python-only: no meaningful cross-language support.
- Version-fragile: a pickle referencing a class that has since moved or changed will fail or silently misbehave.
- Not human-readable in the protocols anyone actually uses.
- Poor long-term archival: depends on your code remaining importable and unchanged.
- No schema or validation: nothing describes what a file should contain.
Related Formats
File Information
Python pickle
Data
.pkl, .pickle
application/octet-stream
Related File Types
Other file types in the Data category you might also need:
Start Analyzing PICKLE Files Now
Use our free AI-powered tool to detect and analyze Python pickle files instantly with Google's Magika technology.
⚡Try File Detection Tool