How to Load Pickle Files Safely in Python

This code demonstrates how to load pickle files safely in Python by using a restricted unpickler that only allows specific, trusted classes, preventing arbitrary code execution from untrusted pickles.

Medium Python 3.9+ Aug 9, 2026 Files & data 14 views 0 copies

Python code

37 lines
Python 3.9+
import pickle

# Default pickle.load is unsafe: it executes arbitrary code when unpickling.
class Unsafe:
    def __reduce__(self):
        return (eval, ("open('/tmp/pickle_demo.txt', 'w').write('pwned')",))

# Create a malicious payload (simulating untrusted source)
malicious_data = pickle.dumps(Unsafe())

# Safe approach: use a restricted unpickler that only allows specific classes
class SafeUnpickler(pickle.Unpickler):
    ALLOWED_GLOBALS = {"builtins": {"list", "dict", "set", "tuple", "int", "str", "bool", "NoneType"}}

    def find_class(self, module, name):
        if module in self.ALLOWED_GLOBALS and name in self.ALLOWED_GLOBALS[module]:
            return super().find_class(module, name)
        raise pickle.UnpicklingError(f"Blocked class: {module}.{name}")

# Demonstrate the attack vector (commented out for safety)
# pickle.loads(malicious_data)  # This would write 'pwned' to /tmp

# Show safe loading with allowed classes
safe_data = pickle.dumps({"name": "Alice", "scores": [85, 92, 78]})
try:
    result = SafeUnpickler(io.BytesIO(safe_data)).load()
    print("Safe load result:", result)
except pickle.UnpicklingError as e:
    print("Blocked:", e)

# Show that unsafe classes are rejected
try:
    SafeUnpickler(io.BytesIO(malicious_data)).load()
except pickle.UnpicklingError as e:
    print("Blocked malicious payload:", e)

import io

Output

stdout
Safe load result: {'name': 'Alice', 'scores': [85, 92, 78]}
Blocked malicious payload: Blocked class: __main__.Unsafe

How it works

The pickle module is document unsafe for untrusted data because it can execute arbitrary code during unpickling. The SafeUnpickler class overrides find_class, which is called for every class reference during unpickling, and only allows classes explicitly listed in ALLOWED_GLOBALS. This restricts what modules and names can be imported, blocking malicious payloads. The demo creates a malicious class that would write a file, but the safe unpickler rejects it before it can execute.

Common mistakes

  • Using `pickle.load` or `pickle.loads` directly on untrusted data without any restriction.
  • Forgetting to import `io` when working with `BytesIO` for pickled data.
  • Making `ALLOWED_GLOBALS` too permissive, allowing modules like `os` or `subprocess`.

Variations

  1. Use `pickletools.optimize` to reduce pickle size for storage or transmission.
  2. Consider using `json` or `msgpack` instead of pickle for untrusted data, as they are safer formats.

Real-world use cases

  • Loading cached model artifacts or preprocessed data in machine learning pipelines where the pickles come from trusted internal sources.
  • Deserializing configuration objects or user session data in a web backend without executing untrusted code.
  • Transferring complex Python objects between services where you control the producer and can enforce class restrictions.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Files & data

Related tutorials and quizzes for this topic.