【セキュリティ】npmセキュリティ入門|サプライチェーン攻撃を防ぐ6つの基本設定
こんにちは。今日も記事をご覧いただきありがとうございます。
今日は、最近学んだ サプライチェーンリスク についてアウトプットを兼ねてまとめてみようと思います。
サプライチェーンリスクとは、ざっくり言うと 自分が依存しているパッケージ経由で被害を受けるリスク のことです。
私はフロントエンド開発でよく npm パッケージを使うのですが、これも立派にサプライチェーンリスクを抱えています。 今回は npm(Node.js のパッケージ管理ツール)パッケージのサプライチェーンリスク にしぼって、現実的な回避策まで整理してみます。
これからセキュリティを学ぼうという方や、ジュニア・ミドルのエンジニアの参考になれば幸いです。
サプライチェーンリスクとは
冒頭でも触れた通り、サプライチェーン攻撃とは、自分のコードではなく、依存しているパッケージ経由で悪意あるコードが混入するタイプの攻撃です。
料理にたとえると、自分のレシピは正しくても、仕入れた食材の中に知らないうちに混入物が入っている ようなイメージかもしれません。
npm の文脈では、たとえばこんな経路があります。
- 人気パッケージのメンテナアカウントが乗っ取られ、悪意あるコードを入れられる
- パッケージ名のタイポ(打ち間違い)を狙った偽パッケージ(typosquatting と呼ばれる手口)をうっかりインストールしてしまう
- 推移的依存に紛れ込んだ不正コードが、
postinstall(パッケージインストール時に自動実行されるスクリプト)などで動いてしまう
何が怖いのか
怖いのは、自分のコードを 1 行も書き換えなくても被害に遭う可能性がある という点だと思います。
npm install した瞬間に、攻撃者のスクリプトが手元の環境で動いてしまうこともあり得ます。
過去にも実例があります。
- event-stream 事件(2018年): 広く使われていたパッケージのメンテナ権限が第三者に譲渡され、依存先の
flatmap-stream経由で、特定の暗号通貨ウォレット(Copay)を狙う悪意あるコードが混入しました - ua-parser-js 事件(2021年): メンテナアカウントが乗っ取られ、暗号通貨マイナーやパスワード窃取を行う不正なバージョンが一時的に公開されました
- chalk / debug など 18 パッケージ事件(2025年9月、通称「Qix」攻撃): メンテナがフィッシングで乗っ取られ、週間 26 億ダウンロードを誇る
chalkdebugほか 18 パッケージに悪意あるコードが混入。発覚から修正まで約 2 時間ほどでしたが、その間に大量のプロジェクトが汚染版を取り込みました - Shai-Hulud ワーム(2025年9月): 自己増殖型のワーム的な挙動で、最終的に 500 近くの npm パッケージに影響したとされる事件です
実践的な回避策
ここからは まず最低限やっておきたいライン を中心に紹介します。
1. package-lock.json を必ず Git にコミットする
package-lock.json は、いわゆるロックファイルで、依存パッケージのバージョン(と推移的依存も含めたツリー全体)を固定する役割があります。
package.json が「こういうパッケージが欲しい」という 依頼書 だとすれば、package-lock.json は「実際にこのバージョンを入れました」という 納品リスト に近いイメージです。
これがないと、同じ npm install でも別のバージョンが入ることがあります。コミットしておくことで、開発環境と CI/本番で同じ依存ツリーを再現できます。
2. CI(継続的インテグレーション) では npm ci を使う
CI(継続的インテグレーション。GitHub Actions などの自動実行環境のことです)では、npm install ではなく npm ci を使うのがおすすめです。
npm install は package.json と package-lock.json に差分があるとロックファイルを更新することがありますが、npm ci はロックファイル通りに厳密にインストールし、不整合があればエラーで止まります。
また、npm ci は node_modules を一度消してからクリーンに入れ直すので、再現性が高いのも利点です。
3. npm audit を習慣化する
npm audit は、今あるパッケージの既知の脆弱性を報告してくれるコマンドです。
週次や PR ごとに走らせると脆弱性に気付きやすくなります。
npm audit fix は、ロックファイルを書き換えて互換性のある範囲で修正します。破壊的変更が必要なケースは --force を付けないと直らないので、その場合は手動で確認したいところです。
.npmrc で audit のしきい値を設定しておくのも便利かもしれません。high 以上の脆弱性が検出されたときだけ非ゼロ終了させたい、といった使い方ができます。
4. --ignore-scripts の検討
--ignore-scripts はその名の通り、インストール時に実行されるスクリプトを除外するオプション です。
通常、npm install をすると依存パッケージ側の preinstall / install / postinstall といったスクリプトが自動で実行されます。これは便利な仕組みですが、もし悪意あるパッケージが紛れ込んでいた場合、その瞬間に攻撃者のコードが手元の環境で動いてしまう という入り口にもなります。
--ignore-scripts を付けると、こうした自動実行を一旦止めることができます。
npm install --ignore-scripts
.npmrc に書いておけば、毎回付け忘れる心配もなくなります。
ignore-scripts=true
必要なパッケージだけ個別にビルドし直すやり方と組み合わせると、リスクをかなり抑えられるかもしれません。
5. Dependabot / Renovate で更新を自動化
古い依存を放置すると、それ自体がリスクになります。
Dependabot や Renovate はパッケージの更新を教えてくれるツールです。 npm audit もパッケージの脆弱性を報告するコマンドですが、手動で実行しなければいけません。 Dependabot や Renovate は PullRequestや定期的にスキャンしてくれる仕組みです。
GitHub なら Dependabot、より細かく制御したいなら Renovate を入れておくと、PR ベースで継続的に追いかけられます。
Dependabot は GitHub にネイティブ統合されていて、.github/dependabot.yml を置くだけで動きます。
Renovate は GitHub App としてインストールする方式で、グルーピングや自動マージ条件など、設定の自由度が高いのが特長です。
min-release-ageの導入も視野に
ここからは、最近の npm に追加された比較的新しいオプション、min-release-age を紹介します。
クールダウン期間とは
min-release-age は、新しくリリースされたパッケージが「使えるようになる」までの待機時間(クールダウン期間)を設定するオプション です。
たとえば「リリースから 7 日経っていないバージョンはインストールしない」と設定しておくと、npm install 時に新しすぎるバージョンは自動でスキップされます。
なぜクールダウンが効くのか
過去のサプライチェーン攻撃を振り返ると、悪意あるバージョンが publish されてから検知・unpublish されるまでの時間はかなり短い ケースが多いです。
たとえば 2025 年 9 月の chalk / debug の事件では、悪意あるバージョンが publish された約 2 時間後にコミュニティが気付き、メンテナによって取り下げられました。2026 年 3 月の axios の事件でも、悪意あるバージョンが流通したのは数時間オーダーです。
つまり、publish 直後の数日を「待つ」だけでも、汚染版を踏むリスクをかなり下げられる という発想です。min-release-age はこの「数日待つ」を仕組みとして強制する設計と言えます。
設定方法
.npmrc に設定します。値の単位は 日数 で、たとえば 7 日にしたい場合は次のように書きます。
注意点
便利な反面、いくつかバランスを見たいポイントもあります。
- 最新バージョンがすぐには使えなくなるので、緊急のバグ修正やセキュリティパッチを入れたい場面では遅延が気になる ことがあります
min-release-ageは npm v11.10.0 で追加された比較的新しいオプションなので、古い npm を使っている環境では効かないかもしれません(pnpm 10.16 / Yarn 4.10 にも同等機能があります)- 値を大きくしすぎると更新が滞るので、3 日〜2 週間あたりで運用に合うところを探る のが現実的かもしれません
CI と開発環境で値を変える、Renovate のクールダウン機能と組み合わせるなど、運用に馴染む形で取り入れてみるとよさそうです。
おわりに
以上が、npm パッケージについてのサプライチェーンリスクの解説でした。
サプライチェーンリスクは、フロントエンド開発をしている以上、誰もが当事者だと思います。
一度に全部やろうとせず、ロックファイルのコミット → npm ci → npm audit の習慣化 あたりから始めるだけでも、防御力はかなり変わってきます。そこに --ignore-scripts や min-release-age のような新しい仕組みを少しずつ足していけば、無理なく強化していけるはずです。
セキュリティはゴールがある分野ではないので、少しずつ学びながらシステムを安全に保っていきましょう。
最後までお読みいただきありがとうございました。 また次の記事でお会いしましょう。