Pickle-десериализация в Python: риски RCE, реальные уязвимости и методы защиты
📋 Кратко
Модуль pickle в Python — удобный, но опасный инструмент сериализации. Из-за опкодов GLOBAL и REDUCE он позволяет выполнять произвольный код на сервере. В статье разбирается механика атаки через __reduce__, реальные CVE, а также методы защиты: отказ от pickle, RestrictedUnpickler, HMAC-подпись и переход на safetensors для ML-моделей.
📌 Зачем нужна сериализация и в чём проблема pickle
Сериализация — фундаментальный механизм в разработке: преобразовать объект в поток байтов для хранения в файле, передачи по сети или сохранения в кеше (Redis, Memcached). Python предлагает для этого несколько встроенных инструментов, и самый популярный из них — модуль pickle. Он умеет сериализовывать практически любые объекты Python: экземпляры классов, функции, вложенные структуры — в компактный бинарный формат. Казалось бы, идеальное решение для кеширования, очередей задач (Celery), распределённых вычислений и обмена данными между микросервисами.
Однако у этого удобства есть оборотная сторона. Официальная документация Python предупреждает прямым текстом: «The pickle module is not secure. Only unpickle data you trust.» — модуль небезопасен, доверяйте только проверенным данным. В отличие от JSON или XML, pickle не просто восстанавливает данные — он восстанавливает произвольные объекты Python, вызывая их конструкторы и методы инициализации. Это открывает прямую дорогу к удалённому выполнению кода (RCE).
Проблема усугубляется тем, что pickle используется в тысячах Python-пакетов и фреймворков — от библиотек машинного обучения (PyTorch, scikit-learn, TensorFlow) до систем очередей (Celery, RQ) и веб-фреймворков (Django). Разработчики часто не осознают, что передача pickle-данных через внешний интерфейс равносильна предоставлению удалённого шелла.
🔍 Как работает pickle и где скрыта опасность
Чтобы понять, почему pickle опасен, нужно разобраться в его архитектуре. Модуль реализует бинарный протокол, который состоит из последовательности опкодов (opcodes) — компактных инструкций для виртуальной машины pickle (Pickle Machine, PM). Процесс десериализации — это не разбор данных, а исполнение программы на этой виртуальной машине.
Протокол pickle и опкоды
Pickle-протокол прошёл 5 версий (0–5), каждая добавляет новые опкоды и оптимизации. Базовые операции включают:
PROTO— версия протоколаFRAME— граница фрейма (протокол 4+)EMPTY_DICT,EMPTY_LIST,INT,BINUNICODE— константыGLOBAL— импорт глобального объекта (модуль + имя)REDUCE— вызов функции с кортежем аргументовSTOP— завершение
Именно опкоды GLOBAL и REDUCE делают pickle опасным. GLOBAL позволяет импортировать любую функцию из любого модуля — от os.system до subprocess.call. А REDUCE вызывает её с произвольными аргументами. Комбинация этих опкодов даёт злоумышленнику полный контроль.
Магия __reduce__
В основе эксплуатации лежит метод __reduce__. Это специальный метод Python, который класс может реализовать для управления собственной сериализацией. Он возвращает кортеж: (callable, args), где callable — функция, а args — кортеж аргументов для её вызова. При десериализации pickle вызывает эту функцию с переданными аргументами.
Злоумышленник может создать класс с __reduce__, который возвращает (os.system, ("rm -rf /",)) или (subprocess.call, (["bash", "-c", "зловредная_команда"],)). При десериализации такого объекта произойдёт выполнение произвольной команды.
⚙️ Практическая эксплуатация: пример атаки
Рассмотрим конкретный сценарий. Предположим, веб-приложение принимает на вход сериализованные данные через API:
import pickle
from flask import Flask, request
app = Flask(__name__)
@app.route('/load', methods=['POST'])
def load_data():
data = request.get_data()
obj = pickle.loads(data) # ⚠️ опасный вызов
return {'status': 'ok'}
Атакующий создаёт вредоносную pickle-строку:
import pickle
import os
class Exploit:
def __reduce__(self):
return (os.system, ('id',))
payload = pickle.dumps(Exploit())
# payload можно отправить на /load
После десериализации payload на сервере выполнится команда os.system('id') — и это лишь минимальный пример. С тем же успехом атакующий может запустить reverse shell, загрузить вредоносное ПО или извлечь данные из базы.
pickletools для дизассемблирования pickle-потоков. Вызов python -m pickletools malicious.pickle покажет все опкоды и поможет определить, содержит ли поток REDUCE с опасными аргументами. Инструмент fickling от Trail of Bits автоматизирует анализ и создание вредоносных pickle-файлов.
📊 Реальные уязвимости и инциденты
Pickle-десериализация — не теоретическая угроза. За последние годы зафиксирован ряд критических CVE, связанных именно с этим вектором атаки:
- CVE-2019-9740 — уязвимость в Python-пакете
webargs: десериализация пользовательских данных через pickle в некоторых конфигурациях приводила к RCE. CVSS 9.8 (Critical). - CVE-2020-28502 — пакет
xmltodictверсии до 0.12.0 содержал код, использующийpickle.loadsна непроверенных данных из XML. CVSS 9.8. - CVE-2022-40897 — Python-пакет для работы с установкой зависимостей содержал уязвимость pickle-десериализации через setup.py. CVSS 9.8.
- CVE-2023-48022 — уязвимость в пакете
huggingface_hub: злонамеренные pickle-модели в Hugging Face Hub могли выполнять код при загрузке черезtorch.load. Этот инцидент привлёк внимание к проблеме безопасности ML-моделей. - CVE-2024-27306 — уязвимость в библиотеке
numpy(старые версии .npy-формата), использующей pickle внутри для сериализации объектов Python в массивах.
Особого внимания заслуживает ситуация с Hugging Face. В 2023–2024 годах исследователи безопасности неоднократно демонстрировали, что загрузка pickle-модели из публичного репозитория может привести к RCE. Формат .pkl является стандартным для многих ML-фреймворков — PyTorch использует pickle для torch.load() до сих пор (с опцией weights_only=True, добавленной в Python 3.12).
🛡️ Методы защиты от pickle-атак
Защита от pickle-десериализации строится на нескольких уровнях: от полного отказа от pickle до безопасной его эксплуатации в изолированной среде.
Уровень 1: Замена pickle на безопасные форматы
Самый надёжный способ — не использовать pickle вообще для работы с непроверенными данными. Python предлагает несколько альтернатив:
- JSON (
json): безопасен по умолчанию, поддерживается всеми языками. Минус: только примитивные типы (dict, list, str, int, float, bool, None). Подходит для 95% задач веб-разработки. - MessagePack (
msgpack): бинарный формат, компактнее JSON, поддерживает байтовые строки. Безопасен, так как не восстанавливает произвольные объекты. - Protocol Buffers (
protobuf): строгая схема, эффективная сериализация, поддержка многих языков. Требует определения .proto-файлов, но исключает атаки десериализации. - CBOR (
cbor2): стандарт IETF RFC 8949, компактнее JSON, поддерживает бинарные данные. Используется в COSE (CBOR Object Signing and Encryption). - Apache Avro: сериализация со схемой, популярна в Kafka-экосистеме. Безопасна и эффективна.
Уровень 2: Безопасная десериализация в защищённой среде
Если отказаться от pickle невозможно (например, нужно взаимодействовать с легаси-системами), применяются методы sandboxing:
- RestrictedUnpickler (Python 3.14+) — новый API, добавленный в Python 3.14. Позволяет переопределить метод
find_class(), чтобы ограничить, какие классы разрешено импортировать и десериализовать. Реализация:
import pickle
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == "myapp.safe" and name in ALLOWED_CLASSES:
return super().find_class(module, name)
raise pickle.UnpicklingError(f"Запрещён: {module}.{name}")
def safe_loads(data):
return RestrictedUnpickler(io.BytesIO(data)).load()
- HMAC-подпись — подписывать pickle-данные секретным ключом перед передачей и проверять подпись при десериализации. Это гарантирует целостность данных, но не защищает, если секретный ключ скомпрометирован:
import hmac, pickle, hashlib
SECRET = b"my-secret-key"
def secure_dumps(obj):
data = pickle.dumps(obj)
sig = hmac.new(SECRET, data, hashlib.sha256).digest()
return sig + data
def secure_loads(signed_data):
sig, data = signed_data[:32], signed_data[32:]
expected = hmac.new(SECRET, data, hashlib.sha256).digest()
if not hmac.compare_digest(sig, expected):
raise ValueError("Подпись недействительна")
return pickle.loads(data)
Уровень 3: Для ML-моделей — safetensors
В машинном обучении проблема pickle стоит особенно остро. Формат .pkl десятилетиями был стандартом. Решение — safetensors, формат, разработанный Hugging Face специально для безопасного хранения тензоров. Он:
- Не содержит исполняемого кода — только данные тензоров
- Поддерживает отображение в память (mmap) для эффективной загрузки
- В 2–10 раз быстрее pickle при загрузке больших моделей
- Широко поддерживается: PyTorch, TensorFlow, PaddlePaddle, JAX
Также стоит рассмотреть ONNX (Open Neural Network Exchange) — формат для обмена моделями между фреймворками, который не использует pickle.
📋 Практические рекомендации для разработчика
На основе анализа CVE, рекомендаций OWASP и документации Python можно сформулировать следующий чек-лист безопасности:
- Не используйте pickle для межсервисного общения — никогда не передавайте pickle-данные через HTTP API, очереди сообщений (RabbitMQ, Kafka) или микросервисные взаимодействия. Замените на JSON, Protobuf или Avro.
- Не принимайте pickle от клиентов — браузерные приложения, мобильные клиенты и внешние API не должны отправлять pickle-данные на сервер. Используйте JSON + JWT-подпись для сессий.
- Изолируйте среду десериализации — если pickle критичен (легаси), используйте RestrictedUnpickler с белым списком допустимых классов или выполняйте десериализацию в отдельном процессе с минимальными привилегиями (контейнер, chroot).
- Подписывайте pickle-данные — HMAC с секретным ключом гарантирует, что данные не были изменены. Ключ должен быть уникален для каждого сервиса и храниться в защищённом хранилище (Vault, AWS Secrets Manager).
- Для ML — переходите на safetensors — начиная с 2024 года Hugging Face рекомендует safetensors как безопасную замену pickle для моделей. PyTorch 2.x поддерживает
torch.load(..., weights_only=True)в качестве дополнительной защиты. - Аудит кода и зависимостей — проверяйте все зависимости на вызовы
pickle.loads()с пользовательскими данными. Используйте SCA-инструменты (Snyk, Trivy, Safety) для автоматического поиска уязвимых версий пакетов. - Используйте pickletools для анализа — при подозрении на вредоносные pickle-файлы запускайте
python -m pickletools подозрительный.pklдля просмотра опкодов. НаличиеREDUCE,GLOBALилиINSTс вызовомos.system,subprocessилиexec— красный флаг. - Обновляйте Python-рантайм — Python 3.14 добавил
RestrictedUnpicklerв публичный API. Старые версии (3.8 и ниже) не имеют даже базовых механизмов ограничения десериализации.
🔮 Будущее безопасной сериализации в Python
Экосистема Python движется в сторону постепенного отказа от pickle как формата обмена. Ключевые тренды 2025–2026 годов:
- PEP 757 — предложение по улучшению модуля pickle с явным указанием разрешённых классов при вызове
pickle.loads(). В Python 3.14 частично реализован черезUnpickler.find_class. - Hugging Face Safetensors — к июлю 2026 года более 80% моделей в Hub используют safetensors. PyTorch, TensorFlow и JAX добавили нативную поддержку формата.
- PyTorch 3.0 (анонсирован на 2027 год) — планирует сделать
weights_only=Trueзначением по умолчанию дляtorch.load(). - Стандартизация сериализации — всё больше Python-фреймворков (FastAPI, Django REST framework) рекомендуют JSON + Pydantic вместо pickle для сериализации объектов.
Тем не менее, pickle остаётся частью стандартной библиотеки и не будет удалён. Он полезен для внутреннего кеширования в доверенной среде, где источник данных находится под полным контролем разработчика. Ключевое правило простое и неизменное: никогда не десериализуйте данные, которым вы не доверяете.
📚 Читайте также
- Управление зависимостями в 2026: SCA и SBOM для поиска уязвимостей в библиотеках
- Устаревший PHP в WordPress: 70% сайтов под угрозой взлома через уязвимости
- Безопасность MCP-протокола для AI-агентов: новые угрозы 2026 года
- Защита CI/CD пайплайнов: как предотвратить компрометацию поставки кода
- Почему уязвимости ИИ-приложений так часто критичны: анализ и практика защиты
📖 Термины
OWASP · Pickle · RCE (Remote Code Execution) · Десериализация · Приватность · Шифрование
🔗 Источники
- CVE-2019-6446 — NumPy Pickle Arbitrary Code Execution (2026-07-18 ✓)
- OWASP Deserialization Cheat Sheet (2026-07-18 ✓)
- OWASP — Deserialization of Untrusted Data (2026-07-18 ✓)
- PyTorch Security Advisory — Pickle Deserialization RCE (GHSA-53q9-r3pm-6pq6) (2026-07-18 ✓)
- Python 3.14 — модуль pickle (security warning) (2026-07-18 ✓)