Какие риски есть при использовании pickle для сериализации в Celery
Что проверяют
Понимаете ли вы, что десериализация pickle это выполнение кода, и где в Celery проходит граница доверия. Следом спрашивают, почему Celery по умолчанию использует JSON и что передавать в задачу вместо объектов.
Как отвечать
Начните с факта: с Celery 4.0 сериализатор по умолчанию JSON, а accept_content по умолчанию принимает только json. pickle включают сознательно, и тогда появляются риски:
- выполнение произвольного кода: при распаковке
pickleвызывает то, что указано в__reduce__объекта. Воркер, который принимаетpickle, выполнит код любого, кто может записать сообщение в брокер. Redis без пароля в общей сети или открытый RabbitMQ превращаются в удалённое выполнение кода на всех воркерах; - хрупкость при деплое: в сообщении лежат путь к модулю и имя класса. Переименовали класс или перенесли модуль, и задачи, уже стоящие в очереди, падают при распаковке. То же при разных версиях Python или библиотек у продюсера и воркера;
- устаревшие данные: объект модели Django, переданный в задачу, содержит состояние на момент постановки, а выполнится задача, может быть, через минуты;
- размер: сериализуется весь граф объектов,
DataFrameна сотни мегабайт раздувает очередь в Redis и замедляет брокер; - переносимость: такие сообщения читает только Python.
Что делать: оставить json в task_serializer, result_serializer и accept_content и проверить result_accept_content для бэкенда результатов. В задачу передавать идентификаторы и пути к файлам в хранилище, а данные доставать внутри. Если нужны бинарные данные, подойдёт msgpack. Если pickle неизбежен, закройте брокер паролем и сетью и подписывайте сообщения: в Celery есть сериализатор auth с ключом и сертификатом.
Где спотыкаются
Говорят, что pickle опасен только для данных от пользователей. Источник данных здесь брокер, и опасен любой, кто может в него писать.
Советуют dill или cloudpickle как безопасную замену. Это тот же механизм с расширенными возможностями, риск тот же.
Переключают task_serializer на json, но оставляют pickle в accept_content. Воркер по-прежнему распакует подброшенное сообщение.