- Published on
npmのサプライチェーン攻撃が消えない理由 — npm-scanが実際に見るもの、そして本当に効く防御
- Authors

- Name
- Youngju Kim
- @fjvbn20031
はじめに — npmのサプライチェーン攻撃は例外ではなく構造だ
npmのサプライチェーン侵害の知らせは、もはやニュースというより背景雑音に近くなりました。人気パッケージが乗っ取られ、インストールしただけでトークンが漏れ、数日後にはまた別のパッケージで同じことが起きます。これが不運の連続でない理由は、npmの設計そのものが攻撃に広い面を差し出しているからです。本稿では、先日 Hacker News で話題になった npm-scan という道具を入り口に、その面が具体的にどこなのか、そして道具でふさげる部分とふさげない部分がどこまでかを、正直に見ていきます。
先に一つ断っておきます。npm-scan の README は自信に満ち、検証しにくい数値を前面に出します。私はこの道具が狙う問題は本物だと考えますが、その数値をそのままは受け取りません。問題と道具を分けて見ましょう。
なぜnpmは構造的に脆いのか
三つが重なります。第一に、インストールスクリプトです。npm パッケージは preinstall、install、postinstall フックで任意のコードを実行でき、そのコードはインストールを実行した利用者の権限で動きます。npm install はまさに、信頼していないコードの実行なのです。第二に、推移的依存関係です。直接選ぶのが10個でも node_modules には数百個が入り、あなたはすでにその数百人の保守者を信頼すると署名したことになります。第三に、タイポスクワッティングです。lodash と lodahs のように一文字違いの名前は、たった一度の打ち間違いで悪性パッケージを呼び込みます。
それぞれは単独なら管理できます。危ういのは、これらが組み合わさる点です。タイポスクワッティングされた名前がパッケージを引き込み、その postinstall がインストール時に走り、しかもそれが信頼していた何かの推移的依存として紛れ込むため、誰も見直しません。三つの弱点は別々の三つのリスクというより、一本のパイプラインなのです。
これが理論でないことを、2025年の Shai-Hulud ワームが示しました。Palo Alto Unit42 の分析によると、このワームはインストールスクリプトで実行され、盗んだ npm トークンでレジストリに認証し、同じ開発者が保守する別のパッケージへ悪性コードを注入して自己複製しました。標的は .npmrc の npm トークン、GitHub の PAT、そして AWS・GCP・Azure の API キーでした。11月の「Shai-Hulud 2.0」は実行時点を postinstall から preinstall へ移し、Unit42 の言葉では、それが「人間の関与を完全になくし、事実上あらゆるビルドサーバーでの実行を保証」しました。2.0 は数万の GitHub リポジトリにまたがり、約350人の利用者にわたって2万5千を超える悪性リポジトリを生み出しました。
この三つの下には、四つ目の要因があります。保守者アカウントです。こうしたキャンペーンの多くは、あなたのコードのバグを見つけて始まるのではなく — フィッシングされたログインや漏れたトークンで信頼された公開者を乗っ取り、あなたがすでに依存するパッケージの悪性バージョンを上げるところから始まります。正規のアカウントがそれを公開した瞬間、上のあらゆる仕組みがそれを自動であなたへ運びます。だからリスクの単位は、個々のパッケージではなくサプライチェーン全体なのです。
肝心なのは、これらがどれも特別なゼロデイを必要とせず、npm が意図的に備える機能 — インストールスクリプトと公開権限 — をそのまま使った点です。だから消えないのです。
npm-scanが実際に見るもの(と、検証できないもの)
npm-scan は @lateos/npm-scan として配布される、ほぼ JavaScript 製の CLI 道具です。掲げる標語は「npm audit、Snyk、Socket が見逃すものを捕まえる」。使い方は単純です。
npm install -g @lateos/npm-scan
npm-scan axios # 単一パッケージを検査
npm-scan scan-lockfile # ロックファイル全体を検査
npm-scan express --json > findings.json
README がうたうのは「23個の検出器(D1–D25)」による静的・行為分析です。完全にローカルで動き、テレメトリもクラウド依存もなく、CI 実行あたり30秒未満を目標にするとされます。出力面は実務的です — SARIF(GitHub Security 連携)、CycloneDX・SPDX の SBOM 出力、YAML のポリシー許可リスト、GitHub Actions 連携。狙う脅威の分類も本稿の前半とちょうど重なります。資格情報の窃取、難読化やアンチデバッグ、メモリからの OIDC・AI キーの抽出、そしてワーム伝播です。
ここまでは妥当です。問題は、README が各分類に98%や99%といった検出率を付けている点です。これらの数値には、公開された方法論もデータセットもベンチマークもありません。何を、どの標本に対して、何を真陽性として測ったのかが見えないなら、それは測定ではなく主張です。さらに README は誤検知(false positive)や限界に一切触れません。どんなスキャナにも誤検知と見逃しがあります。強みだけを並べるセキュリティ道具は、それ自体が警戒の理由です。個別の検出器 D1–D25 が何をどう捕まえるのかも、README だけでは分かりませんでした — 詳細としてリンクされた文書は、私が確認した時点で開けませんでした。
最後にライセンスです。個人・オープンソース・評価用は MIT で無料ですが、本番利用には年 $799 から $9,900 の商用ライセンスが要ります。CI に常時組み込む前に知っておくべき値です。
言い回しについて一言。「npm audit、Snyk、Socket が見逃すものを捕まえる」は中立的な記述ではなく競争上の主張です — Socket と Snyk は自前の行為分析を備えた成熟した道具であり、「見逃す」という語が多くを背負っています。「既存の道具が捕まえられないものを我々は捕まえる」といった売り込みは、結論ではなく、あなた自身の依存で試すべき仮説として受け取るのが妥当です。
本当に効く防御
スキャナは防御の一層にすぎず、本気の攻撃者はヒューリスティックを回避します。ブラスト半径を実際に縮めるのは、退屈な基本です。
- インストールスクリプトを切る。
.npmrcにignore-scripts=trueを書くか、npm install --ignore-scriptsを使えば、Shai-Hulud 系が頼る実行点そのものが消えます。ただで済むわけではなく — ネイティブモジュールのように本当にビルド手順が要るパッケージは例外扱いが必要ですが、たいていの依存にスクリプトは要りません。 - ロックファイルと
npm ci。package-lock.jsonをコミットし、CI ではnpm installではなくnpm ciを使います。固定された木をそのまま入れ、ドリフトがあれば失敗します。PR でロックファイルの差分を人が読む習慣をつければ、思わぬ新しい推移的依存が目に留まります。 - 来歴(provenance)を検証する。 npm の provenance と信頼された公開は、パッケージをそれをビルドした CI に結び付けます。
npm audit signaturesで署名を確認できます。 - 依存を減らす。 もっとも根本的です。あらゆる推移的依存は攻撃面であり、信頼すべき保守者が一人増えることです。一行のユーティリティのためにパッケージを足す前に、その面を広げる価値があるか問いましょう。
- トークンを管理する。 ビルドマシンに
.npmrcのトークンを放置しないこと。範囲を狭め、定期的に回転させます。Shai-Hulud がまさにそのファイルを探したことを思い出してください。
この一覧は、おおむね効き目の順です。インストールスクリプトの遮断が最初なのは、それが最近のワームが通ったまさにその扉を閉めるからで、依存を減らすことが最後なのは、重要でないからではなく効果が最も遅く出るからです — 長い目で見れば、残りすべてを縮めてくれるのはその項目です。
npm-scan、Socket、Snyk のようなスキャナは、この基本の上に重ねる一層としては価値があります。ただ、スキャナがこの一覧を置き換えることはありません。
おわりに
npmのサプライチェーン攻撃が消えないのは、それがバグではなく機能の悪用だからです。インストールスクリプトと公開権限は、npm を便利にしているまさにその要素であり、攻撃者はそれを意図どおりに使います。だからこれは「完治」する問題ではなく、管理される問題です。
とはいえ悲観する話ではありません。趨勢は悪化しています — pre-install 実行と自己複製するワームは明確な格上げです — が、対策はよく知られており、ほとんど無料です。足りないのは知識ではなく、事故が起きるまでこの退屈な衛生がなかなか優先されない、という点なのです。
npm-scan のような道具への向き合い方も、同じ姿勢が正しいと思います。狙う問題は本物で、ローカル実行・SARIF・SBOM 出力といった設計は妥当です。しかし方法論のない検出率と、限界についての沈黙は、信頼ではなく検証を求めます。スキャナは一層として置き、ブラスト半径を実際に縮めるのはロックファイルとスクリプト遮断、来歴、そして少ない依存だと忘れないこと — それが正直な結論です。