NPMで8万6,000回超ダウンロードされた悪性パッケージが大量拡散
(arstechnica.com)- NPMリポジトリで100個以上の認証情報窃取用の悪性パッケージが8月以降未検出のままアップロードされ、合計8万6,000回以上ダウンロードされていたことが確認された
- セキュリティ企業 Koi は、「PhantomRaven」と名付けた攻撃キャンペーンがNPMの Remote Dynamic Dependencies(RDD) 機能を悪用し、126個の悪性パッケージを配布したと報告
- RDDは、パッケージが信頼されていないドメインから依存コードを動的にダウンロードできる仕組みで、静的解析ツールでは検出されない
- 攻撃者はこの機能を利用してHTTP接続による悪性コードのダウンロードを行い、パッケージのメタデータ上では「0 Dependencies」と表示されるため、開発者やセキュリティスキャナーが認識できなかった
- このような構造的脆弱性は、NPMエコシステムにおけるセキュリティ管理の限界と自動インストール機構の危険性を浮き彫りにしている
NPMリポジトリ内での悪性パッケージ拡散
- 攻撃者がNPMコードリポジトリの構造的な弱点を利用し、8月以降100個以上の認証情報窃取用パッケージをアップロード
- 大半のパッケージは未検出のまま配布され、累計ダウンロード数は86,000回以上
- セキュリティ企業 Koi はこの攻撃を PhantomRavenキャンペーン と命名し、NPMの特定機能が悪用されたと分析
- Koiによれば、126個の悪性パッケージのうち約80個が記事執筆時点でもなおNPM上に残っていた
Remote Dynamic Dependencies(RDD)の脆弱な構造
- RDDは、パッケージが外部Webサイトから依存コードを動的にダウンロードすることを許可する機能
- 通常、依存関係はNPMの信頼されたインフラから取得されるが、RDDはHTTPなどの暗号化されていない接続でのダウンロードも許可する
- PhantomRavenの攻撃者はこの機能を使い、悪性URL(例:
http://packages.storeartifact.com/npm/unused-imports)からコードをダウンロードするよう設定- こうした依存関係は開発者やセキュリティスキャナーから見えず、パッケージ情報には「0 Dependencies」と表示される
- NPMの自動インストール機能により、このような**「見えない」依存コードが自動実行**される構造になっている
セキュリティツールの検出限界
- Koiの Oren Yomtov は、「PhantomRavenは既存のセキュリティツールの死角を巧妙に悪用した事例だ」と述べた
- RDDは静的解析ツールでは検出されない
- その結果、攻撃者はセキュリティ検証を回避しながら悪性コードを配布できた
追加の脆弱要因
- Koiは、RDDで取得された依存関係はインストールのたびに攻撃者のサーバーから新たにダウンロードされると説明
- キャッシュやバージョン管理がないため、同じパッケージでもインストール時点ごとに異なる悪性コードが注入される可能性がある
- このような動的ダウンロード構造は、パッケージ完全性の検証を困難にする
NPMの構造と背景
- NPMはJavaScript向けのパッケージマネージャーで、GitHub子会社の npm, Inc. が管理
- Node.jsの標準パッケージマネージャーであり、コマンドラインクライアントと npm registry で構成される
- registryには公開および有料の非公開パッケージが保存され、Webサイトから検索できる
- 今回の事件は、NPMの自動依存関係管理の仕組みが攻撃に悪用され得ることを示す事例として指摘されている
その他の言及
- 記事の末尾では、不要なJavaScriptの実行をブロックすべきだという意見に言及
- しかし今回の攻撃は、必須のJavaScriptコードすら悪用された事例だと指摘されている
2件のコメント
リアルタイムスキャナースクリプトを作ってみました。
疑わしいリポジトリのpathで
npx sha1-hulud-scannerを入力すれば大丈夫です。
ソースコード : https://github.com/developerjhp/sha1-hulud-scanner
Hacker Newsのコメント
最近、自分は
npmコマンドを Dockerコンテナ内で実行するように alias を設定しているこうすると環境変数を露出せず、現在のディレクトリ外のファイルにもアクセスせず、
.bashrcのような設定ファイルにも触れられない参考: Run tools inside Docker
npmはすぐに実行される 任意コード をダウンロードするのだからその代わり
pnpmを勧める。デフォルトで lifecycle スクリプトを実行せず、許可するスクリプトを ホワイトリスト 指定できる本当に保護したいなら、インストールだけでなく実行全体をサンドボックス内で回すべきだ
今のように post-install だけを止めるのは半端な対策にすぎない。サプライチェーン攻撃はますます危険になっている
neovimやvscodeが感染すれば、すでにユーザー権限で十分危険なことができる単純な alias は
node/npmには効くが、他のプログラムには適用しづらい。コンテナに必要なリソースをマウントしなければならないからだ依存関係自体が感染しているかもしれない
前から不思議だった。なぜ人は平然と
npmをシステム上で動かすのかmakeのような 再現可能なビルド に慣れている立場からすると、npmが毎回違うものをダウンロードし、違う結果を出すのは衝撃だったCSS 生成すら npm 依存に結びつけるのは妙だった。だから Docker の中に npm 環境を丸ごと 固定(freeze) してみたが、結局は負け戦のように思える
maven、nuget、pip、npmはみな同じだ昔のようにディストリビューションのパッケージマネージャに依存していたら、今のような 高速なエコシステム は不可能だっただろう
ただしセキュリティを強化した新しいパッケージマネージャも登場している。理由を理解せずに手段だけを非難するのは正しくない
npmを Docker に固定したなら、依存関係を更新するたびにその環境を 検証 したのか聞きたい実際には
npmとpnpmはすでにデフォルトで lock ファイル によって依存関係を固定しているnpm install thing” があまりにも簡単で安いことだ多くのオープンソースが 品質より履歴書向けコード で埋まり、結局は巨大企業の広告トラッカーやウォレットアプリのようなものを作るのに使われる
npm installは単にパッケージをダウンロードするのではなく コードを実行するpackage.jsonの preinstall、install、postinstall フックが実際に実行される合法的にインストール過程で任意コマンドを実行しなければならない理由とは何だろう?
関連レポート: PhantomRaven npm malware
別の事例: Socket.devブログ
たとえば Linux カーネルパッケージは、インストール後に initramfs の再生成、GRUB の更新 などのため post-install スクリプトを実行する
ほとんどの DEB/RPM パッケージにこうしたスクリプトが含まれている。つまり設計自体の問題だ
npmは 誰でもパッケージを公開できること だLinux ディストリビューションには信頼できる メンテナ体制 があり、PGP ベースのルート・オブ・トラストを自前で構築することもある
一方で
npm、pip、rubygems、cargoなどは、実質的には “curl | bash” の洗練版にすぎない保守負担を減らすために、こうした post-install ビルドが必要だった
Package.swiftファイルを実際に実行するただし強く サンドボックス化 されているので、悪用は難しいと聞く
参考: SwiftPMドキュメント, PackageDescription
pnpm v10はデフォルトで全 lifecycle スクリプトを無効化しており、ユーザーが自分で許可しなければならない関連する議論
最近の
npm攻撃を見ると、もうnpmで開発するのは 安全なのか と思ってしまうReact プロジェクトを始めるたびに数百個のパッケージが入るのに、何をしているのか分からない
バックエンドでは必要なパッケージだけを明示的に入れるが、フロントエンドは 脆弱性のパンドラの箱 のようだ
npmが一番大きく、ニュースになりやすいだけだjjをインストールしたら 470 個、Python のwan2gpは 211 個のパッケージが入った。みな似たり寄ったりだxz事件のように、各依存関係が 見ず知らずの個人 に委ねられており、その人たちがソーシャルエンジニアリング攻撃に遭わないと信じるしかないPyPIも安全ではない。GitHub Actions のハックによって 正規パッケージに悪性コードが挿入された事例 もあるAngular や Vue のようなフレームワークで開発するたびに不安になる
node_modulesの中の何千もの依存関係を見ると 災厄の前触れ のように感じるオープンソース開発者が 1 人フィッシングに引っかかれば、すぐ感染するかもしれない
JavaScript エコシステムは 根本的に壊れている。タイプミス 1 つでサプライチェーン攻撃にさらされる
NuGet や Maven でも不可能ではないが、あちらは 標準ライブラリ が大きく、依存関係が少ないので統制感がある
完璧ではないが、それでも一段ましだ
86,000 回のダウンロードの大半は、実ユーザーではなく 自動化スキャナやボット である可能性が高い
新バージョンを上げると 1、2 日で数百回ダウンロードされるが、実際の人間ではないかもしれない
つまり感染したユーザーはほとんどいない可能性もある
AI チャットボットが 幻覚で作り出したパッケージ名 を狙った攻撃も多い。単純な統計以上の話だ
より詳しい攻撃の説明は BleepingComputerの記事 を参照
npm install中に HTTP URL を依存関係として使うパッケージを検出またはフィルタリングする方法はあるだろうかリクエスターごとに異なるペイロードを送れるため、一般的なスキャナでは検出が難しい
趣味の開発者として、こうした サプライチェーン攻撃への備え をどうすべきか悩んでいる
有名なチュートリアルに従って依存関係を入れていくうちに、いつの間にかセキュリティへの注意が薄れてしまう
自分のホームラボでも複数のサービスを動かしているが、もしボットが侵入したらと心配だ。どこから始めればいいだろう?
完璧な保証ではないが、サーバー全体を突破されるよりはずっとましだ
必要なコードだけを自分でコピーして使うのも、良い学習になり安全な方法だ
こうした構造なら、何百万ものユーザーが直接検証しなくても ディストリビューションレベルで信頼性を確保 できる
例: Hono, Zod
自分は最近 Bun に移行したが、DB ドライバや S3 クライアントのようなものが標準で組み込まれていて、追加ダウンロードが減る
lifecycle フックで依存関係を持ってくる構造は、いつでも 攻撃の転換点 になりうる
今は正常でも、後で所有者がハックされたり気が変わったりすれば、悪性コードに変わる可能性がある
この種のインストールフックは結局 持続不可能な設計 だ