6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

サプライチェーン攻撃対策の多層防御CIアプローチ実装ガイド

Last updated at Posted at 2025-12-15

はじめに

この記事は 株式会社TRAILBLAZER Advent Calendar 2025 の記事です。

TRAILBLAZERでフロントエンドエンジニアをしている田原です。

2024年以降、Node.jsエコシステムにおけるサプライチェーン攻撃が急増しています。特にShai-Huludと呼ばれるマルウェアは、npmパッケージのpreinstallスクリプトを悪用し、開発者のマシンやCI環境に感染を広げており、2025年12月15日(現在)もHotな話題となっております。

本記事ではプロジェクトで実装した攻撃対策としての多層防御アプローチを解説していきます。

対象読者

  • Node.js/npmを使用しているプロジェクトの開発者
  • CI/CDパイプラインのセキュリティを強化したい方
  • サプライチェーン攻撃の具体的な対策を知りたい方

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

サプライチェーン攻撃は、ソフトウェアの依存関係(サードパーティライブラリ)を通じて悪意のあるコードを混入させる攻撃手法です。

主な攻撃パターン

  1. 悪意あるパッケージの公開 - 人気パッケージに似た名前(typosquatting)で悪意あるコードを含むパッケージを公開
  2. 既存パッケージの乗っ取り - メンテナのアカウント侵害やソーシャルエンジニアリングによるパッケージ乗っ取り
  3. 依存関係の汚染 - 間接的な依存パッケージに悪意あるコードを混入

Shai-Huludの例

Shai-Huludマルウェアは、以下のような手法で感染を広げました:

{
  "scripts": {
    "preinstall": "node malicious-script.js"
  }
}

npm installを実行するだけで、preinstallスクリプトが自動実行され
環境変数の窃取やバックドアの設置が行われます。

多層防御アプローチ

単一の対策では完全な防御は困難です。今回は以下の7層で防御を実装を試みております。

対策 防御タイミング 目的
1 save-exact=true npm install時 バージョン固定
2 ignore-scripts=true npm install時 悪意あるスクリプト実行阻止
3 OSV-Scanner CI 既知の脆弱性検出
4 gitleaks CI シークレット漏洩検出
5 Semgrep CI 危険なコードパターン検出
6 Aikido Safe Chain CI 24時間ルール・マルウェア検知
7 e18e CI 依存関係の分析

ツール取得方法の方針

CIでセキュリティツールを取得する際、取得方法自体がサプライチェーン攻撃のリスクとなります。

curl | sh パターンは危険です

# ❌ 危険な例
curl -fsSL https://example.com/install.sh | sh

この方法は、外部URLが改ざんされた場合に悪意あるスクリプトが実行される可能性があります。

本実装では以下の優先順位でツールを取得しています:

優先度 取得方法 説明
1 GitHub Action バージョン固定でGitHubのセキュリティ審査を受けたActionsを使用
2 GitHub CLI gh release downloadで公式リリースから取得(GitHub API経由で認証付き)
3 npmパッケージ npmレジストリから取得
4 curl(直接URL) ❌ 禁止

各ツールの取得方法

ツール 取得方法 理由
OSV-Scanner GitHub CLI 公式ActionはReusable Workflow形式で制約あり
gitleaks GitHub CLI 公式Actionは組織リポジトリで有料ライセンス必要
Semgrep GitHub Action 公式提供あり
Aikido Safe Chain npm パッケージ 公式GitHub Action未提供
e18e npx CLIツールとして提供

実装詳細

1. バージョン固定(save-exact=true)

なぜ必要か?

package.jsonでよく見る^~は、セマンティックバージョニングに基づいて自動的に新しいバージョンをインストールする仕組みです。

{
  "dependencies": {
    "express": "^4.18.0"
  }
}

この場合npm install時に4.19.04.20.0など、メジャーバージョンが同じ最新版がインストールされます。攻撃者がパッケージを乗っ取り、悪意あるコードを含む4.19.0をリリースした場合、自動的に感染する可能性が増加してしまいます。

設定方法

.npmrcファイルをプロジェクトルートに作成:

save-exact=true

既存プロジェクトへの適用

既存のpackage.jsonから^~を削除する必要があります:

{
  "dependencies": {
-   "express": "^4.18.0",
-   "axios": "~1.6.0"
+   "express": "4.18.0",
+   "axios": "1.6.0"
  }
}

その後、npm installを実行してpackage-lock.jsonを更新します。

2. スクリプト実行阻止(ignore-scripts=true)

なぜ必要か?

npmパッケージはpreinstallpostinstallなどのライフサイクルスクリプトを定義できます。これらはインストール時に自動実行されるため、攻撃の起点となります。

設定方法

.npmrcに追加:

save-exact=true
ignore-scripts=true

注意点

ネイティブモジュール(node-gypを使用するパッケージなど)はビルドスクリプトが必要な場合があります。その場合は個別に対応が必要です。

# 特定のパッケージのみスクリプトを許可してインストール
npm install --ignore-scripts=false <package-name>

3. OSV-Scanner(脆弱性スキャン)

OSV-ScannerはGoogleが開発したオープンソースの脆弱性スキャナーです。

機能

  • package-lock.jsonをスキャンしてCVE脆弱性を検出
  • OSV(Open Source Vulnerabilities)データベースを使用

GitHub Actionsでの実装

osv-scanner:
  name: OSV-Scanner (脆弱性スキャン)
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    - name: Install OSV-Scanner via GitHub CLI
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        gh release download v2.0.2 --repo google/osv-scanner --pattern 'osv-scanner_linux_amd64'
        chmod +x osv-scanner_linux_amd64

    - name: Run OSV-Scanner
      run: |
        ./osv-scanner_linux_amd64 --lockfile=package-lock.json --lockfile=setowa-web/package-lock.json --format=table

ポイント

  • GitHub CLIで公式リリースから取得: gh release downloadはGitHub API経由で認証付きリクエストを行うため、curlより安全
  • --format=tableでCIログに見やすく出力
  • 公式GitHub Action(google/osv-scanner-action)はReusable Workflow形式で提供されており、他ジョブと同一ファイルで使用する際に制約があるため、CLI版を使用

4. gitleaks(シークレット検出)

gitleaksは、Git履歴からAPIキーやパスワードなどの機密情報を検出するツールです。

GitHub Actionsでの実装

gitleaks:
  name: gitleaks (シークレット検出)
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Install gitleaks via GitHub CLI
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        gh release download v8.21.2 --repo gitleaks/gitleaks --pattern 'gitleaks_8.21.2_linux_x64.tar.gz'
        tar -xzf gitleaks_8.21.2_linux_x64.tar.gz
        chmod +x gitleaks

    - name: Run gitleaks
      run: |
        if [ "${{ github.event_name }}" = "pull_request" ]; then
          ./gitleaks detect --source . --log-opts="origin/${{ github.base_ref }}..HEAD" --verbose
        else
          ./gitleaks detect --source . --verbose
        fi

ポイント

  • fetch-depth: 0でGit履歴全体を取得
  • PR時は変更コミットのみ、push時はリポジトリ全体をスキャン
  • 公式GitHub Action(gitleaks/gitleaks-action)は組織リポジトリで有料ライセンスが必要なため、GitHub CLI経由でCLI版を使用

5. Semgrep(静的解析)

Semgrepは、コードパターンを検出する静的解析ツールです。XSSやSQLインジェクション、eval()の使用など、セキュリティ上問題のあるコードを検出できます。

GitHub Actionsでの実装

semgrep:
  name: Semgrep (静的解析)
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    - name: Run Semgrep
      uses: semgrep/semgrep-action@v1
      with:
        config: p/typescript p/javascript p/security-audit
        publishToken: ''
      env:
        SEMGREP_RULES: p/typescript p/javascript p/security-audit

使用ルールセット

  • p/typescript - TypeScript固有のルール
  • p/javascript - JavaScript固有のルール
  • p/security-audit - セキュリティ監査用ルール

ポイント

  • 公式GitHub Action(semgrep/semgrep-action)を使用: 問題なく利用可能
  • publishToken: ''でSemgrep Cloudへの結果送信を無効化

6. Aikido Safe Chain(マルウェア検知)

Aikido Safe Chainは、npmパッケージのマルウェア検知に特化したツールです。

主な機能

  • 24時間ルール: 公開から24時間以内のパッケージをブロック(新規公開パッケージは悪意ある可能性が高い)
  • マルウェア検知: Aikido Intelデータベースでリアルタイム検証

GitHub Actionsでの実装

aikido-safe-chain:
  name: Aikido Safe Chain (マルウェア検知)
  runs-on: ubuntu-latest
  defaults:
    run:
      working-directory: ./setowa-web
  steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-node@v4
      with:
        node-version-file: .node-version
        cache: npm

    - name: Install Aikido Safe Chain via npm
      run: npm install -g @aikidosec/safe-chain

    - name: Install dependencies with Safe Chain
      run: safe-chain npm ci

ポイント

  • npmパッケージとしてインストール: 公式GitHub Actionが提供されていないため、@aikidosec/safe-chainをnpm経由でインストール
  • safe-chain npm ciで依存パッケージを検証しながらインストール

7. e18e(依存関係分析)

e18eは、依存関係の分析ツールです。
node_modulesの肥大化などについて検知を行います。

GitHub Actionsでの実装

e18e:
  name: e18e (依存関係分析)
  runs-on: ubuntu-latest
  defaults:
    run:
      working-directory: ./setowa-web
  steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-node@v4
      with:
        node-version-file: .node-version
        cache: npm

    - name: Run e18e analyze
      run: npx @e18e/cli analyze

完成したワークフロー

すべてを統合した.github/workflows/security.yml

name: security

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  osv-scanner:
    name: OSV-Scanner (脆弱性スキャン)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install OSV-Scanner via GitHub CLI
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release download v2.0.2 --repo google/osv-scanner --pattern 'osv-scanner_linux_amd64'
          chmod +x osv-scanner_linux_amd64

      - name: Run OSV-Scanner
        run: |
          ./osv-scanner_linux_amd64 --lockfile=package-lock.json --lockfile=setowa-web/package-lock.json --format=table

  gitleaks:
    name: gitleaks (シークレット検出)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install gitleaks via GitHub CLI
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release download v8.21.2 --repo gitleaks/gitleaks --pattern 'gitleaks_8.21.2_linux_x64.tar.gz'
          tar -xzf gitleaks_8.21.2_linux_x64.tar.gz
          chmod +x gitleaks

      - name: Run gitleaks
        run: |
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            ./gitleaks detect --source . --log-opts="origin/${{ github.base_ref }}..HEAD" --verbose
          else
            ./gitleaks detect --source . --verbose
          fi

  semgrep:
    name: Semgrep (静的解析)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        uses: semgrep/semgrep-action@v1
        with:
          config: p/typescript p/javascript p/security-audit
          publishToken: ''
        env:
          SEMGREP_RULES: p/typescript p/javascript p/security-audit

  aikido-safe-chain:
    name: Aikido Safe Chain (マルウェア検知)
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./setowa-web
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version-file: .node-version
          cache: npm

      - name: Install Aikido Safe Chain via npm
        run: npm install -g @aikidosec/safe-chain

      - name: Install dependencies with Safe Chain
        run: safe-chain npm ci

  e18e:
    name: e18e (依存関係分析)
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./setowa-web
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version-file: .node-version
          cache: npm

      - name: Run e18e analyze
        run: npx @e18e/cli analyze

運用上の注意点

検出時のCI失敗設定

導入初期は検出してもCIを失敗させない設定にすることを推奨します。これにより

  1. まず検出結果を確認
  2. 誤検知の調整
  3. 段階的にCI失敗設定を有効化

という流れで運用できます。

まとめ

サプライチェーン攻撃への対策は、単一のツールでは不十分です。本記事で紹介した多層防御アプローチにより、以下の防御を行っています。

攻撃パターン 対策
意図しないバージョン更新 save-exact=true
悪意あるインストールスクリプト ignore-scripts=true
既知の脆弱性 OSV-Scanner
シークレット漏洩 gitleaks
危険なコードパターン Semgrep
新規公開パッケージ・マルウェア Aikido Safe Chain
依存関係の可視化 e18e

これらの対策を組み合わせることで、サプライチェーン攻撃のリスクを大幅に軽減できます。

補足

ニュースになってからKyouhei Fukuda氏の以下の記事で内容と防御アプローチについてキャッチアップを行いました。(なので幾つかは以下記事にも書かれていることです)
ありがとうございました🙇

また、以下の記事(なぎ氏)の内容についても参考にさせて頂きました。
ありがとうございました🙇

最後に

本記事を最後まで読んで頂きありがとうございます:bow:

TRAILBLAZERでは一緒に働くメンバーを募集中です!!
皆さまからのご連絡お待ちしております:train:

参考資料

6
0
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
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?