MENU

サプライチェーン攻撃で被害に遭わないためのnpm security best practice~OWASP Cheat Sheetに基づく~

Securityチーム オーナーの松田です。
昨今、Webアプリケーション開発者にとって大きな脅威となっているのがサプライチェーン攻撃です。侵害されたパッケージをinstallすることにより、主に開発者のローカルPCに保存された認証情報やCI/CDパイプライン上の認証情報が窃取される可能性があります。特に、npmで公開されているパッケージの侵害は大きな話題になっています。

たとえば、2026年3月末に話題になったaxiosの侵害です。axiosは、JavaScriptでWebアプリケーションを開発している方であれば、一度は目にしたことがあるのではないでしょうか。非常に有名なパッケージだったため、大きな話題になりました。

出典:
npm上のaxiosが侵害される:メンテナンス担当者のアカウントが乗っ取られ、RATが展開された
https://jp.aikido.dev/blog/axios-npm-compromised-maintainer-hijacked-rat

この記事は、これらの一連のnpmに対するサプライチェーン攻撃の被害に遭わないようにするためにOWASP Cheat Sheet Seriesのnpm security best practiceから対策について学びます。


目次

OWASP Cheat Sheet – npm Security Best Practices

  1. Avoid publishing secrets to the npm registry
  2. Enforce the lockfile
  3. Minimize attack surfaces by ignoring run-scripts
  4. Assess npm project health
  5. Audit for vulnerabilities in open source dependencies
  6. Artifact governance and supply chain protections
  7. Responsibly disclose security vulnerabilities
  8. Enable 2FA
  9. Use npm author tokens
  10. Understanding typosquatting and slopsquatting attacks
  11. Use trusted publishers for secure package publishing
  12. Prevent dependency confusion attacks

出典:
NPM Security best practices
https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html

12個もありますが、この中には、npmパッケージのメンテナー向けの項目とnpmパッケージを利用する開発者向けの項目と混在しています。そのため、本記事では、これら12項目を「npmパッケージを利用する開発者」という視点で再分類します。


超重要(必ず実施すべき)

2. Enforce the lockfile

lockfileを強制しましょう。lockfileは、「どのバージョンの依存関係を使うか」を固定化する仕組みです。これにより、複数の開発者間でバージョンを固定化することができるようになりました。

一連のサプライチェーン攻撃は、基本的には侵害された新しいバージョンが公開されます。つまり、lockfileを強制しバージョンが固定化することによって、「意図しないバージョンの取得」を防ぐことで、侵害された新しいバージョンを不用意にインストールしてしまうリスクを下げることができます。

しかし、実は、npm installでは、プロジェクトとlockfileの間に不整合が検出された場合、package.jsonを基準に依存解決を再計算し、lockfileを書き換る可能性があります。そのため、lockfileを強制する必要があります。

以下のコマンドは、いずれもlockfileを強制し、不整合がある場合は失敗に終わります。

yarn install --frozen-lockfile
npm ci

基本的に、CI/CD環境や新しく環境を構築する場合は、これらのコマンドを使いましょう。


3. Minimize attack surfaces by ignoring run-scripts

run-scriptsを無視することにより、アタックサーフェスを最小化しましょう。

npmパッケージには、インストール時に自動実行される postinstall などのライフサイクルスクリプトを含められます。便利な仕組みですが、ここに悪意あるコードが含まれていると、npm install しただけで任意コード実行につながります。サプライチェーン攻撃で最も悪用されやすいポイントの一つです。

特に危険なのは、被害がアプリケーション実行時ではなく「開発者の端末」や「CI/CD環境」でのinstall時に起きることです。.npmrc や環境変数、クラウド認証情報、GitHub Actionsのシークレットなどが窃取対象になります。つまり、アプリケーションそのものがまだ動いていない段階でも、npm install だけで攻撃が成立します。しかも、lockfileでバージョンを固定していても、正規に追加したパッケージ自体が悪性であれば防げません。

そのため、新規パッケージを検証するときや、まだ十分に信頼できない依存関係を試すときは、まず npm install --ignore-scripts を使うのがいいでしょう。また、常時無効化したい場合は .npmrcignore-scripts=true を設定しておき、どうしてもライフサイクルスクリプトが必要なパッケージだけを個別に見極めるのがいいでしょう。
特に、プロジェクトルートに置いた .npmrc はそのプロジェクトに参加する他の開発者にも共有しやすいため、プロジェクト単位で安全側の設定を強制しやすい点がいいですね。

ただし、ignore-scripts=true は万能ではありません。npmエコシステムには、postinstall などを使ってネイティブバイナリの取得やセットアップを行うパッケージが存在します。たとえば esbuild は、--ignore-scripts 付きでもある程度動作するよう調整されていますが、これは install script が完全に不要になったという意味ではなく、制約付きで利用できるようにしている、という位置づけです。そのため、すべてのプロジェクトで無条件に常時ignore-scripts=trueにできるとは限りません。プロジェクト単位の .npmrcignore-scripts=true を標準にしつつ、必要な依存関係がある場合だけ例外的に扱う運用がよさそうです。

出典:
esbuild Getting Started
https://esbuild.github.io/getting-started/#additional-npm-flags

また、公開直後のバージョンにすぐ飛びつかないことも重要です。「新しいバージョンをすぐに使わず、少し時間をおいて様子を見る」という考え方です。一連のサプライチェーン攻撃では、新しいバージョンをインストールしてしまうことで被害に遭っています。

npm v11 で min-release-age が追加されました。これは、公開から指定日数が経過していないバージョンをインストール対象から外す機能で、たとえば .npmrcmin-release-age=3 と書いておけば、公開から3日未満の版は自動的に避けられます。公開直後に悪性コードが混入し、短期間で削除されるようなケースへの対策として理にかなっており、こちらもプロジェクト単位の .npmrc に置くことで、チーム全体に同じルールを適用しやすくなります。

そのため、プロジェクトルートに以下の内容の.npmrcファイルを置いておくといいでしょう

# 公開から7日未満のバージョンはインストールしない
# ただし、急遽バージョンアップが必要な場合は、一時的に無効化する
min-release-age=7
# postinstallやpreinstallを無効化
# ただし、必要なパッケージを含む場合は、例外対応する
ignore-scripts=true

min-release-ageは、比較的新しいため、npmのバージョンや実行環境によっては利用できない場合があります。


5. Audit for vulnerabilities in open source dependencies

オープンソースの依存関係内の脆弱性をチェックしよう。有名パッケージであっても、脆弱性が後から見つかることは珍しくありません。そのため、「悪意あるパッケージを避ける」だけでなく、「正当なパッケージに含まれる既知の脆弱性を継続的に検出する」ことも必要です。

npm audit で、依存関係に既知の脆弱性がないか確認しましょう。

ただし、npm audit fix を機械的に実行すればよいわけではありません。メジャーバージョン更新や挙動変更を伴う場合は、差分確認やテストが必要です。
ローカルでの npm audit に加えてCIでの定期実行も組み合わせ、GitHub Dependabotのような監視機能と併用するのが現実的です。特に重要なライブラリでは、修正版が出ているかだけでなく、回避策があるか、メンテナーがどの程度迅速に対応しているかまで見ておくと判断しやすくなります。


10. Understanding typosquatting and slopsquatting attacks

typosquattingは、既存の有名パッケージと見分けがつきにくい名前で悪意のあるパッケージを公開し、タイプミスや見落としでインストールさせる攻撃です。たとえば axios のつもりで別名を入れてしまえば、それだけで侵害につながり得ます。

slopsquattingは、AIアシスタントがもっともらしく提案した実在しないパッケージ名を、攻撃者が先回りして公開し、それを利用者にインストールさせる攻撃です。npmでは似た名前のパッケージも多く、見た目には自然に見える名前で確認漏れを誘発しやすいため、typosquattingと同様に注意が必要です。一度インストールすると、run-script や依存関係経由で被害が広がる可能性があります。

そのため、インストールコマンドは手打ちや記憶ではなく、公式ドキュメントや信頼できるリポジトリから確認するべきです。npm view <package> や npmレジストリ画面で、実在するか、公開者、更新履歴を確認するだけでも事故はかなり減らせると思います。ダウンロード数が極端に少ない、GitHubリポジトリの実体が乏しい、Issue活動がほぼないといったパッケージは慎重に扱うべきです。特に、AIが提案したパッケージ名はそのまま信用せず、必ず人間が裏取りしてください。


12. Prevent dependency confusion attacks

dependency confusionは、社内用パッケージと同名のパッケージを公開npmに登録し、そちらを誤って取得させる攻撃です。特に、社内パッケージ名がスコープなしで使われていたり、.npmrc のregistry設定が曖昧だったりすると、npmは「より優先度の高いレジストリ」を選択するため、意図せずpublic registryが優先されることがあります。

これは企業における開発では非常に重要です。被害は「開発者が誤って入れる」だけでなく、CI/CDが自動的に悪性パッケージを取得してしまう形でも起きます。内部用パッケージ名は、GitHub公開物や求人情報、ログなどから推測されることがあり、一度公開レジストリ側が選ばれると、通常のインストール処理として侵害されます。

そのため、内部パッケージは @company/package のようにスコープ付きにし、.npmrc には @company:registry=https://<private-registry> を明示して、解決先を曖昧にしないことが重要です。加えて、可能であれば内部で使うパッケージ名を public npm 側で予約しておくと、攻撃者による先取りも防ぎやすくなります。

出典:
npm Docs – About scopes
https://docs.npmjs.com/about-scopes/

npm Docs – scope

https://docs.npmjs.com/cli/v11/using-npm/scope

重要(継続運用で効く)

4. Assess npm project health

npmプロジェクトの健全性を評価する。この項目は、いま自分たちが使っているnpm環境や依存関係の状態を定期的に点検することです。つまり、開発者側の環境に問題がないか、依存関係が古くなりすぎていないかを確認するための運用です。

このとき役立つのが npm outdatednpm doctor です。npm outdated は、プロジェクトルートで実行すると、現在インストールされている依存関係に対して、更新可能なバージョンがあるかどうかを確認できます。これは既知脆弱性を調べる npm audit とは役割が異なり、「依存関係が古くなりすぎていないか」を把握するためのコマンドです。古いから即危険というわけではありませんが、更新候補を長期間放置すると、後で差分が大きくなり、セキュリティ修正も取り込みにくくなります。

$ npm outdated
Package                      Current   Wanted   Latest  Location                                  Depended by
@eslint/js                    9.32.0   9.39.4   10.0.1  node_modules/@eslint/js                   front
@mui/icons-material            7.3.0   7.3.10    9.0.0  node_modules/@mui/icons-material          front

一方の npm doctor は、依存パッケージそのものではなく、npmを使う実行環境を点検するコマンドです。registryへの接続、Node.js や git の実行可否、キャッシュの破損、権限設定などを確認するため、npm installnpm audit の結果が不安定なときの切り分けにも役立ちます。つまり、npm outdated が依存関係の棚卸し、npm doctor がnpm利用環境の健康診断という位置づけです。実務では、依存関係の更新状況を定期的に確認しつつ、npmの動作自体に不調がないかも合わせて点検する運用にするとよいでしょう。

$ npm doctor
Connecting to the registry
Ok

Checking npm version
Not ok
Use npm v11.12.1

Checking node version
Not ok
Use node v24.15.0 (current: v24.12.0)

Checking configured npm registry
Ok
using default registry (https://registry.npmjs.org/)

6. Artifact governance and supply chain protections

アーティファクトのガバナンスとサプライチェーンの保護する。この項目は、依存関係の取得元や本体をどのように、検証するかという話です。

Use a local npm proxy

local npm proxyは、組織内で依存関係の取得先を制御するための仕組みです。OWASP のnpm security best practiceの記事内では、Verdaccio のようなローカルプロキシを例に挙げています。公開npmへの直接アクセスを減らし、キャッシュされた既知の依存を再利用できます。利用可能なパッケージの範囲を組織で制御しやすくなるので、突然公開された不審パッケージが即座に全端末へ配布されるリスクも下げられそうです。

Governance & Verification Steps

この項目は、「取得元を制御する」だけでなく、「取得したものやビルドしたものを後から検証できるようにする」ことです。

検証の方法の一つが、npm audit signatures です。これは、ダウンロード済みの依存関係に対して、registry signatures を検証し、利用可能な場合は provenance attestations も検証するコマンドです。たとえば、次のような結果が出ます。

$ npm audit signatures
audited 523 packages in 10s

523 packages have verified registry signatures

37 packages have verified attestations

ここで verified registry signatures は、レジストリが提供する署名を検証できたパッケージ数を示しています。つまり、レジストリ側が提供する署名情報に照らして、取得したパッケージの整合性を確認できたということです。
一方の verified attestations は、provenance のような追加の証明情報まで確認できたパッケージ数です。attestation が付いているパッケージでは、どのリポジトリ・コミット・CI から公開されたかといった情報を追加で追跡できます。

なお、この証明は、npmのページ上でも確認できます。

ちなみに、523個すべてに署名検証が通っていても、そのうち37個しかattestationがありませんね。いくつか試してみましたが、verified attestationsできるパッケージはまだ少ないようです。「そこまで検証可能な形で公開されているパッケージはまだ一部」という理解です。

まとめると、proxyで取得元を統制しつつ、利用者側では署名やprovenanceを確認できるパッケージを評価し、必要に応じて npm audit signatures のような確認手段を使いましょう。


関連知識として押さえるべき(主にメンテナー向け)

その他の項目は、主にnpmパッケージを公開するメンテナー向けのPracticeです。

1. Avoid publishing secrets to the npm registry

npmレジストリに機密情報を公開しないようにしましょう。これは、公開パッケージに .env や認証情報、内部設定ファイルを誤って含めないためのPracticeです。


7. Responsibly disclose security vulnerabilities

脆弱性は、責任をもって開示する。脆弱性を見つけたときに、メンテナーと研究者が調整しながら修正と公開を進めるためのPracticeです。


8. Enable 2FA

2 Factor Authenticationを有効化する。npm公開者アカウントを多要素認証で守るPracticeです。


9. Use npm author tokens

npmのauthorトークンを利用する。npmの認証にユーザー名・パスワードや広すぎる長寿命トークンを使うのではなく、用途を限定した access token を使うべきというPracticeです。特にCI/CDでは、読み取り専用、対象package限定、期限付き、IP制限付きなどの token を使うことで、漏えい時の被害を小さくできます。


11. Use trusted publishers for secure package publishing

セキュアなパッケージ公開のために、trusted publishingを利用する。trusted publishingは、長寿命トークンではなく、GitHub ActionsやGitLab CI/CDのOIDC連携で安全に公開する仕組みです。


まとめ

OWASP Cheat Sheet Seriesのnpm security best practiceでの、npmパッケージを使う開発者の立場で優先順位をつけると、まず実施すべきなのは次の5つです。

  • npm ci などでlockfileを強制すること
  • --ignore-scripts を前提にrun-scriptの実行面を絞ること。また、min-release-age で公開してすぐのパッケージのインストールを控えること
  • npm audit などで既知脆弱性を継続監視すること
  • typosquatting と slopsquatting を前提にパッケージ名を検証すること
  • dependency confusion を避けるためにscopeとregistry設定を明示すること

その上で、追加でプロジェクト健全性の確認やnpm proxyの導入を進めると、日常運用の安全性はさらに上がります。

npmのサプライチェーン攻撃は、特別な組織だけが狙われる話ではありません。普段どおり npm install している開発者が最初の被害者になることも珍しくありません。

だからこそ、依存関係は「便利だから入れる」ではなく、「出所と挙動を確認してから入れる」ように変えていきましょう。

この記事を書いた人
松田 康司

株式会社神戸デジタル・ラボ
デジタルビジネス本部 Securityチーム オーナー / 生産技術チーム

神戸大学情報知能工学科卒。2014年にKDLに入社。自社で構築するECサイトのほぼすべてのプロジェクトに関与しながら、多くの開発プロジェクトを経て、現在はSecurityチーム オーナーと全社のセキュア開発を推進する生産技術チームを兼任。開発部門主導のセキュア開発を実践している。
コミュニティ活動として、OWASP Kansaiボードメンバー、アルティメットサイバーセキュリティクイズ実行委員を務める。

  • URLをコピーしました!
目次