15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

npmサプライチェーン攻撃対策の最初の一歩

15
Posted at

先に結論だけ

npmサプライチェーン攻撃対策の最初の一歩として、以下の3つを設定します。

.npmrc
ignore-scripts=true
min-release-age=7
.github/dependabot.yml
cooldown:
  default-days: 7

追加パッケージは不要です。設定ファイルの変更だけで被害を軽減できます。

はじめに

対象読者

npmを使うNode.js開発者で、サプライチェーンセキュリティ対策をどこから手をつけていいかわからない方を対象とします。サプライチェーン攻撃の仕組みや、セキュリティツールの経験は前提としません。

対象環境

  • npm v11.10.0以降(min-release-ageの利用に必要)
  • GitHubリポジトリでDependabotを利用している環境

npmのバージョンはnpm --versionで確認できます。

npmサプライチェーン攻撃とは

npmパッケージのインストールや更新を悪用して、開発者の環境で悪意あるコードを実行させる攻撃です。攻撃者は正規のパッケージを乗っ取ったり、名前が似た悪意あるパッケージを公開したりして、npm installの実行時にマルウェアを仕込みます。

攻撃は以下の2つの手法を組み合わせて成立します。

  • 悪意ある依存の注入 : 正規パッケージの依存先に悪意あるパッケージを追加し、間接的にマルウェアをインストールさせる
  • ライフサイクルスクリプトの悪用 : 注入されたパッケージのpostinstallスクリプトで悪意あるコードを実行し、npm installだけでマルウェアを起動させる

悪意あるパッケージの多くは公開後短期間で検出・対処されます。以下はいずれも公開当日に検出された事例です。

本記事の対策は、公開直後のパッケージのインストールを遅らせて防御します。コミュニティやセキュリティ企業が短時間で悪意あるパッケージを発見・削除できることがこの防御策の前提です。

攻撃のより詳しい仕組みについては1Password Shell Plugin + GitHub Fine-Grained PATによるnpmサプライチェーン攻撃の感染拡大防止策を参照してください。

対策1: ignore-scripts

概要

npmパッケージにはpreinstallpostinstallなどのライフサイクルスクリプトを設定でき、npm install時に自動実行されます。サプライチェーン攻撃の主要な実行経路がこのライフサイクルスクリプトです。ignore-scripts=trueを設定すると、これらのスクリプト実行を無効化できます。

設定方法

グローバルのnpm設定ファイル~/.npmrcに以下を追加します。

.npmrc
ignore-scripts=true

この設定により、npm install時のライフサイクルスクリプト(preinstallinstallpostinstallなど)が実行されなくなります。npm startnpm testなどのユーザー定義スクリプトは引き続き実行されます。

例外的な有効化

bcryptsharpなどネイティブアドオンを含むパッケージは、ビルドスクリプトの実行が必要です。ignore-scripts=trueの環境ではnpm rebuildで個別にビルドスクリプトを実行します。

npm rebuild sharp

npm rebuildは指定したパッケージのビルドスクリプトのみを実行します。プロジェクト内の全パッケージのスクリプトが実行されるわけではありません。

npm v11時点では、パッケージ単位でスクリプト実行を許可するビルトイン機能はありません。npm RFCs Discussion #80allowedScriptsフィールドが提案されていますが、未実装です。

対策2: min-release-age

概要

min-release-ageはnpm v11.10.0で導入されたオプションで、公開から指定日数が経過していないパッケージのインストールを拒否します。悪意あるパッケージが検出・削除されるまでの猶予を確保する仕組みです。

zizmorの作者William Woodruffの分析によると7日間のクールダウンが推奨されています。

Woodruffの分析はサンプルサイズ10件と小さく、本人も "very small sample set" と認めています。7日間は目安であり、プロジェクトのリスク許容度に応じて調整してください。

設定方法

npm設定ファイル~/.npmrcに以下を追加します。

.npmrc
min-release-age=7

ターミナルから以下のコマンドでも設定できます。

npm config set min-release-age 7 --global

値は日数で指定します。上記の設定では、公開から7日未満のパッケージバージョンのインストールが拒否されます。

min-release-ageとnpx

min-release-ageオプションは、npxコマンドにも影響を与えます。パッケージのダウンロードと即時実行を合わせて行うnpxコマンドでも、min-release-ageオプションが尊重され、この条件を満たさないパッケージは実行されません。

対策3: Dependabot cooldown

概要

Dependabotのcooldownは、パッケージの公開日から一定期間が経過するまでPR作成を遅らせる機能です。CI/CDパイプラインでのnpm installは開発者のローカル.npmrc設定を参照しないため、ignore-scriptsmin-release-ageによる保護が効きません。cooldownはこのCI/CD側の保護を担います。

設定方法

.github/dependabot.ymlcooldownフィールドを追加します。

.github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    cooldown:
      default-days: 7

cooldownはバージョン更新(Dependabot version updates)にのみ適用されます。セキュリティアップデート(Dependabot security updates)には適用されません。セキュリティアップデートは脆弱性修正のため、遅延なく適用されます。

semverレベル別の待機期間

cooldownではsemverレベルごとに待機日数を設定できます。

フィールド 説明
default-days デフォルトの待機日数
semver-major-days majorバージョン更新の待機日数
semver-minor-days minorバージョン更新の待機日数
semver-patch-days patchバージョン更新の待機日数

個別のフィールドが設定されていない場合はdefault-daysの値が使われます。majorバージョン更新は破壊的変更を伴うことが多く、慎重な対応が求められるため、より長い待機期間を設定するのが一般的です。

includeexcludeフィールドで対象パッケージを制限することもできます。excludeincludeより優先されます。詳細はDependabot公式ドキュメントを参照してください。

3つの対策の補完関係

3つの対策はそれぞれ異なるレイヤーと攻撃経路を保護します。

対策 保護対象 防御メカニズム カバーする攻撃経路
ignore-scripts ローカル環境 スクリプト実行の遮断 ライフサイクルスクリプト
min-release-age ローカル環境 時間ベースのゲート 両方
Dependabot cooldown CI/CDパイプライン 時間ベースのゲート 両方

ignore-scriptsはライフサイクルスクリプト経路の攻撃を即座に遮断しますが、悪意ある依存の注入には効果がありません。min-release-agecooldownは時間ベースのゲートとして、経路を問わず悪意あるパッケージが検出・削除される猶予を確保します。

また、ignore-scriptsmin-release-ageはローカルの.npmrcで機能しますが、CI/CDパイプラインにはおよびません。Dependabot cooldownがCI/CD側の保護を担うことで、開発ワークフロー全体をカバーします。

この3つの対策を足がかりに、あなたのプロジェクトに合わせた防御層を重ねていくことで、それぞれの環境に合った防御策を組み上げてください。

まとめ

本記事では、npmサプライチェーン攻撃対策の最初の一歩として3つの設定を紹介しました。

  • ignore-scripts=true: ライフサイクルスクリプトの実行を無効化し、攻撃の主要な実行経路を遮断する
  • min-release-age=7: 公開直後のパッケージのインストールを拒否し、検出・削除までの時間的猶予を確保する
  • Dependabot cooldown: CI/CDレベルで公開直後のバージョンへのPR作成を遅延し、自動更新による意図しないインストールを防ぐ

これらの設定だけでは完全な防御にはなりません。認証情報の保護やGitHubリポジトリへの感染拡大防止については、1Password Shell Plugin + GitHub Fine-Grained PATによるnpmサプライチェーン攻撃の感染拡大防止策を参照してください。

参考

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?