あなたのプロジェクトは、依存パッケージのバージョンをどのように管理していますか?
2026年3月24日、PythonのLLMゲートウェイライブラリ「LiteLLM」がサプライチェーン攻撃を受けました。
PyPI上の侵害されたバージョンは約46分間公開されています。
そして、その間に約47,000回ダウンロードされたとRedditで報告されていました。
本記事では、このインシデントの概要と、開発者が学ぶべき教訓を整理していきます。
何が起きたのか
LiteLLMのバージョン1.82.7および1.82.8に、認証情報を窃取するマルウェアが仕込まれました。
攻撃者はTeamPCPと呼ばれるグループです。
メンテナーのPyPIアカウントを侵害し、正規のCI/CDフローを迂回してパッケージを直接アップロードしたとされています。
特に厄介だったのは、バージョン1.82.8に含まれていた.pthファイルの仕組みです。
Pythonの.pthファイルは、インタープリタの起動時に自動実行されます。
そのため、import litellmと書かなくてもマルウェアが動作してしまうのです。
REPLを開いても動く。
pytestを実行しても動く。
パッケージがインストールされた環境でPythonを起動するだけで、悪意あるコードが走るわけです。
このマルウェアが収集していた情報は、広範囲に及びます。
SSHの秘密鍵、AWS・GCP・Azureのクラウド認証情報、Kubernetesの設定ファイル、.envファイルに記載されたAPIキーなど。
シェル履歴や暗号通貨のウォレットファイルまで、開発環境に存在するあらゆる秘密情報が対象でした。
なぜLiteLLMが狙われたのか
LiteLLMは、100以上のLLMプロバイダーへの統一インターフェースを提供するライブラリです。
月間約9,500万ダウンロードを記録する人気パッケージでもあります。
Redditのコメント欄で指摘されていた点が、的を射ていました。
LiteLLMは設計上、複数のAIプロバイダーのAPIキーをプロキシとして管理しています。
そのため、侵害されたバージョンはそれらの認証情報に即座にアクセスできる立場にありました。
権限昇格すら不要なのです。
攻撃の起点は、セキュリティスキャナー「Trivy」の侵害にさかのぼります。
LiteLLMのCIパイプラインは、Trivyをバージョン固定なしで利用していました。
そこから侵害されたTrivyを経由し、PyPIの公開用トークンが漏洩。
それを使って悪意あるパッケージがアップロードされたのです。
たった一つの管理されていない依存関係から、連鎖的に複数のエコシステムが侵害されました。
47,000ダウンロードの実態
この数字は見出しとしてインパクトがあります。
しかし、実態はもう少し複雑です。
Redditでは、経験豊富な開発者がこの点に疑問を呈していました。
PyPIのダウンロード数には、ミラーサーバーへのダウンロードやセキュリティスキャンも含まれます。
そのため、実際のインストール数とは乖離があるという指摘です。
自分でPyPIにパッケージを公開してみれば分かります。
誰も知らないパッケージでも、数万ダウンロードが記録されることは珍しくありません。
一方で、別の調査報告も出ています。
5,000から10,000のSaaS環境に影響が及ぶ可能性があるとの推計です。
47,000という数字をそのまま受け取るのは過大評価かもしれません。
しかし、影響がゼロだったわけでもないでしょう。
実際にRedditでは「職場のグループがやられた」という報告がありました。
また、「直接の認証情報流出はなかったが、依存していたdatabricks-agentsの関係でIT部門がすべてをロックダウンし、デプロイが止まった」という声も上がっています。
依存関係管理の現実
今回の攻撃で最も教訓的なのは、依存パッケージのバージョン指定に関するデータです。
Redditの投稿者は、LiteLLMに依存する2,337のパッケージを調査しました。
その結果は以下のとおりです。
- 59%が下限のみの指定(>=1.80のような形式)
- 16%が上限を設けていたものの、侵害バージョンを含む範囲
- 12%がバージョン制約なし
- 安全にピン留めされていたのは、わずか12%
つまり、大多数のパッケージは「最新バージョンなら何でも受け入れる」状態にありました。
=1.80の利便性は、サプライチェーンリスクに見合わない。
コメント欄の開発者たちは、口を揃えてそう警告していました。
pip installの落とし穴
多くの開発者は「マルウェア入りパッケージは、importしなければ大丈夫」と考えています。
しかし、今回のケースではそのメンタルモデルが通用しませんでした。
.pthファイルは、pip installの過程でPythonサブプロセスが起動された時点で実行されます。
さらに言えば、pip uninstallでもpip showでもpip listでも発火する。
Pythonインタープリタが起動するたびに動くのです。
Redditのコメントで詳しく解説していた開発者によると、クリーンアップの順序にも注意が必要だといいます。
まず.pthファイルを手動で削除する。
次にマルウェアの永続化デーモンを停止する。
そうして初めて、pipコマンドを安全に実行できるようになります。
認証情報のローテーションは最後です。
永続化スクリプトは約50分間隔でポーリングしています。
動作中にキーをローテーションすると、新しいキーも攻撃者の手に渡ってしまうからです。
救われたケース
今回の攻撃で被害を免れた事例も報告されていました。
ある開発者は、Redditで偶然このニュースを見つけました。
すぐに上層部へエスカレーションし、安全なバージョンに固定したそうです。
「仕事中にサボってニュースサイトを読んでいたら、セキュリティリサーチをしていたことになった」と冗談交じりに報告していたのが印象的でした。
また、JFrog Artifactoryのような社内PyPIミラーを運用しているチームの事例もあります。
新しいバージョンに待機時間を設けていたため、パッケージはミラーにダウンロードされたものの、サンドボックス環境での検査で隔離されました。実害には至っていません。
安定したDockerイメージでバージョン1.82.1を使用しており、ポッドが11日以上再起動されていなかったために偶然救われた、という声もありました。
もしその間にポッドが再起動されていたら、最新版がプルされていた可能性は高いでしょう。
PyPIのセキュリティモデルへの疑問
Redditでは、根本的な疑問も議論されていました。
「なぜ外部の人間による検証なしにパッケージを更新でき、即座に全員に配信されるのか」と。
これに対する反論もあります。
インストールするバージョンは利用者側の責任であり、PyPIが全パッケージのマルウェア監査を行うのは現実的ではない、という意見です。
ただし、改善案もいくつか提案されていました。
「pipがリリース後一定時間経過したバージョンのみインストールする」「人気上位パッケージの更新にはレビュープロセスを設ける」といったものです。
PyPIのセキュリティチームの対応自体は迅速だったことも、忘れてはなりません。
侵害されたバージョンは比較的短時間で隔離されています。
開発者が今すぐやるべきこと
今回のインシデントから得られる実践的な教訓をまとめておきましょう。
まず、依存パッケージのバージョンは厳密にピン留めすべきです。
ただし、単なるバージョン固定では不十分な場合もあります。
ハッシュ検証付きのロックファイル(pip-toolsの–generate-hashes、uv lock、poetry lockなど)を使ってください。
レビュー済みのアーティファクトと完全に一致するものだけをインストールできるようになります。
次に、社内パッケージミラーの導入も検討に値します。
上流の新バージョンに待機時間を設けるか、手動での承認プロセスを挟む。
それだけで、今回のような攻撃の影響を大幅に軽減できるでしょう。
そして、AIツールチェーンのセキュリティには特別な注意を払うべきです。
LiteLLMのように複数のAPIキーを集中管理するツールは、攻撃者にとって格好の標的になります。
こうしたツールが扱う認証情報の範囲を考えれば、通常のライブラリ以上に厳格な管理が求められるでしょう。
まとめ
LiteLLMの侵害は、AIエコシステムにおけるサプライチェーン攻撃の深刻さを示す事例です。
46分間という短い窓でも、CI/CDパイプラインが偶然そのタイミングでビルドを実行すれば被害に遭います。
.pthファイルによる攻撃は、従来のメンタルモデルを覆すものでした。
「importしなければ安全」という前提は、もう通用しません。
便利さとセキュリティのトレードオフを意識し、依存関係の管理を見直す。
地味な作業です。
しかし、その価値は今回のインシデントが証明しています。
