Beyond Model Weights: How Python Serialization Can Become a Security Risk
A machine learning model is often treated as a file that can simply be downloaded, loaded, and used. But what if that file contains more than model weights?
In Python, serialization makes it possible to save objects and restore them later. However, some serialization formats can also preserve instructions that execute during loading. When these files come from untrusted sources, a simple model-loading operation can become a security vulnerability.
Understanding this risk requires looking beyond the model itself and examining the entire ML software supply chain.
1. Serialization: More Than Saving Data
Serialization converts an object into a format that can be stored or transmitted. Deserialization reconstructs the object from that representation.
Python provides several serialization mechanisms, including JSON and Pickle.
import pickle
data = {"model": "classifier", "version": 1}
with open("model.pkl", "wb") as f:
pickle.dump(data, f)
with open("model.pkl", "rb") as f:
restored = pickle.load(f)This example serializes a Python dictionary and restores it later.
The important distinction is that not all serialization formats have the same security properties. JSON represents basic data types, while Pickle can reconstruct complex Python objects using mechanisms that may invoke functions during deserialization.
2. Pickle: When Loading a File Can Execute Code
Pickle is powerful because it can serialize many Python objects, including custom classes and certain functions or references to functions.
However, its flexibility creates a serious security risk: unpickling an untrusted file can execute arbitrary code.
Consider this simplified example:
import pickle
with open("downloaded_model.pkl", "rb") as f:
model = pickle.load(f)The code looks harmless. Yet if an attacker has crafted the file maliciously, the loading operation itself may trigger unwanted behavior.
Depending on the execution environment, this could lead to data theft, credential exposure, or other system compromise.
The vulnerability does not require the model to make a prediction. It can occur before inference even begins.
The key lesson: A file extension such as .pkl tells you little about whether the file is trustworthy. Never load untrusted Pickle files.
3. How This Becomes an ML Supply-Chain Attack
Modern ML applications rarely depend on a single model file. They depend on model repositories, Python packages, training pipelines, configuration files, and external services.
This creates multiple points of attack.
Malicious model files
An attacker distributes a model artifact containing a malicious Pickle payload. A developer downloads it and loads it using an unsafe deserialization method. The attack exploits trust in the artifact.
Dependency confusion
An ML project may depend on several external Python packages. If an attacker publishes a malicious package with a name or version that a dependency-resolution process selects instead of the intended internal package, installing it can execute malicious code. Tools such as pip-audit help identify known vulnerabilities in installed dependencies, although they cannot detect every malicious package or supply-chain attack.
Compromised model repositories
A model repository may contain model weights, configuration files, custom Python code, and instructions for loading the model. Even when the weights themselves are safe, custom loading code or associated dependencies may introduce risk.
These attack paths share a common principle: an attacker exploits trust in the components used to build or run an ML system.
4. Beyond Pickle: Other Risks in the Model Supply Chain
Serialization is only one part of the problem.
- Keras Lambda serialization: Serialized Lambda functions may introduce code-execution risks when loaded unsafely. Risk depends on the format, configuration, and loading options.
- Repository manipulation: An attacker may replace model artifacts, alter configuration files, or publish misleading model metadata.
- API key compromise: Exposed credentials can allow unauthorized access to model-serving APIs or other connected services.
- Prompt injection: Malicious instructions in untrusted content can manipulate an LLM application when that content is interpreted as instructions.
These threats are different mechanisms, but they can intersect in one application. A compromised dependency might expose an API key, while a compromised model repository might introduce unsafe loading code.
Security therefore requires more than checking the model weights.
5. Defending Against Malicious Model Files
The goal is not to avoid serialization altogether. It is to choose appropriate formats and control what the loading process is allowed to do.
- Prefer safer formats. For tensor-based models, Safetensors is designed to store tensors without embedding arbitrary Python object reconstruction. It reduces deserialization risk but does not guarantee that an entire model pipeline is safe.
- Use safer PyTorch loading options. Where supported, use
torch.load(..., weights_only=True)for compatible checkpoints. This restricts the objects that can be loaded, but it is not a universal security guarantee and does not make arbitrary model-loading workflows safe. - Verify the source. Download artifacts from trusted publishers, verify cryptographic hashes or signatures when available, and review repository code before executing it.
- Audit dependencies. Use tools such as
pip-audit, pin dependency versions, and review package sources. A clean audit does not prove that a package is benign. - Isolate untrusted artifacts. If an artifact must be inspected, use a restricted environment with minimal permissions, no unnecessary credentials, and limited network access.
TL;DR
Python serialization simplifies saving and restoring objects, but formats like Pickle can execute arbitrary code during deserialization. In ML systems, this creates risks that extend beyond model files to the entire software supply chain.
Key attack vectors include:
- Malicious model artifacts that execute code when loaded.
- Compromised dependencies that introduce malicious behavior.
- Unsafe loading logic that executes untrusted code.
- Exposed credentials that enable unauthorized access.
To reduce these risks:
- Prefer safer formats like Safetensors for tensor weights.
- Never deserialize untrusted Pickle files.
- Verify artifact provenance and audit dependencies.
- Load untrusted artifacts in isolated, least-privilege environments.
The bottom line: A model loading successfully does not make it trustworthy. Secure ML starts with controlling what you load and what that code is allowed to do.