Две версии LiteLLM — 1.82.7 и 1.82.8 — попали на PyPI уже с зашитым бэкдором. Если у вас в проде крутилась хотя бы одна из них, эта supply chain атака тихо отдала ключи от вашего AI-пайплайна человеку, которого вы никогда не видели: переменные окружения, API-ключи, облачные креды — всё разом. LiteLLM — это шлюз, через который тысячи команд гоняют запросы к OpenAI, Anthropic, AWS Bedrock и Azure OpenAI по одному API. Именно этот охват и сделал историю такой болезненной.
Давайте разберём по порядку: что случилось, как работал бэкдор и что бы я сделал ещё до обеда, найди я эти версии у себя в локфайлах.
Что произошло на самом деле
LiteLLM — самый популярный AI-шлюз на рынке. Сотни моделей, десятки провайдеров, один API. Он сидит в середине вашего inference-стека и по определению держит у себя все креды, которые вы ему скармливаете, — потому что в этом вся его работа.
Атакующий не искал хитрый zero-day в коде. Он зашёл в аккаунт мейнтейнера на PyPI. Без двухфакторки. Один аккаунт, один пароль — и в ту секунду, когда это упало, он смог публиковать что угодно под именем, которому уже доверяли тысячи requirements.txt. Он выложил 1.82.7 и 1.82.8. Все, у кого в этом окне отработал незапиненный pip install litellm, затащили вредоносный код прямо в прод.
Как работал бэкдор
Полезная нагрузка была скучной — и именно поэтому эффективной. Скомпрометированные версии читали окружение — то самое, где живут ваши OPENAI_API_KEY, ANTHROPIC_API_KEY, креды AWS и секреты Azure, — и отправляли их на endpoint атакующего.
Ни падения. Ни ошибки в логах. Ни изменения поведения, за которое зацепился бы мониторинг. Шлюз как маршрутизировал запросы, так и продолжал — и параллельно выносил ваши секреты через чёрный ход. К моменту, когда кто-то это заметил, ключей уже не было.
Что должно напугать вас по-настоящему
Это уже третья крупная supply chain атака на PyPI за год, и рисунок всегда один и тот же:
- Популярный пакет с единственным мейнтейнером
- Аккаунт под защитой одного пароля и больше ничего
- Миллионы установок ниже по цепочке, которые за часы наследуют вредоносную версию
- Команды, которые никогда не проверяют, что именно они затянули
Большинство так ничего и не замечает — вот где настоящая проблема. Если ваш pip install крутится в CI без проверки хешей, вы доверяете гигиене чужого аккаунта свои прод-ключи. Вот и вся модель безопасности, если называть вещи своими именами.
Что делать прямо сейчас
Если LiteLLM у вас есть хоть где-то — сделайте это сегодня, по порядку:
- Проверьте локфайлы. Если стоит 1.82.7 или 1.82.8 — немедленно откатывайтесь на 1.82.6.
- Ротируйте все креды, которые видел LiteLLM. Каждый ключ провайдера, каждый облачный секрет, каждый токен из этого окружения. Считайте, что утекло всё, — потому что так и есть.
- Проверьте исходящий трафик. Поднимите логи кластера и ищите соединения с endpoint'ами, которые вы не узнаёте. Это и есть канал утечки.
- Добавьте проверку хешей в CI/CD. Пиньте точные версии и сверяйте контрольные суммы на каждом
pip install. Не сошёлся хеш — билд падает. Без вариантов.
Ротация ключей — та самая скучная и муторная часть, которую все хотят пропустить. Не пропускайте. Если вредоносная версия отработала хотя бы раз с доступом к креду — этот кред скомпрометирован.
Ваш пайплайн настолько безопасен, насколько его слабейшая зависимость
Я веду 62 подписки Azure в проде. Я своими глазами видел, как одна плохая зависимость расходится каскадом по всему окружению, — и это не то, что хочется дебажить в два часа ночи. Именно поэтому я пиню каждую зависимость и сверяю каждую контрольную сумму, без исключений. Как Certified Ethical Hacker я приучил себя думать как атакующий — а атакующий не ломает ваш код, он ломает доверие, которое вы раздаёте всем, кто стоит выше вас в цепочке.
«Просто pip install» кажется бесплатным. Это не так. Удобство — это кредит под залог вашей безопасности, а supply chain атаки — это ровно тот момент, когда приходит счёт. Каждая незапиненная зависимость — это чужой человек с копией ваших прод-ключей, который просто пока не решил ими воспользоваться.
Ваш AI-пайплайн настолько безопасен, насколько его слабейшая зависимость. На этой неделе такой зависимостью оказался LiteLLM. На следующей будет что-то другое. Так что честный вопрос: когда вы в последний раз реально проверяли целостность своих Python-зависимостей?