mcpField
S
supply-chain-attack-response

supply-chain-attack-response

github.com/bagelholeセキュリティ1.2k308 回インストール23 週間前に更新
リポジトリ

supply-chain-attack-response について

ソフトウェアサプライチェーン攻撃の検出と対応を扱う、開発・セキュリティチーム向けガイドです。依存関係の監査やインシデント対応に加え、ロックファイル、コンテナ、CI/CD の確認方法を紹介します。

主な機能

  1. 01依存関係の監査と不審な挙動の検出
  2. 02パッケージ、コンテナ、ビルドの来歴検証
  3. 03侵害された依存関係の封じ込めと痕跡調査
  4. 04ロックファイルの確認と依存バージョンの固定
  5. 05CI/CD の権限とランナーの強化
  6. 06SLSA 来歴証明の導入

活用シーン

  1. 01侵害が確認された依存関係への対応
  2. 02新規導入するサードパーティーパッケージの監査
  3. 03依存関係の更新後に起きた CI/CD の異常調査
  4. 04リリース成果物の来歴証明の生成と検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill supply-chain-attack-response

インストール後、Claude Code で/supply-chain-attack-responseを入力して読み込まれたか確認します。

詳細

サプライチェーン攻撃への対応

ソフトウェアサプライチェーン攻撃は、開発者が暗黙に信頼している依存関係、ビルドシステム、配布経路を標的にします。PyPI、npm、crates.io のパッケージが侵害されると、その下流のすべての利用者が悪意のあるペイロードを受け継ぎます。このスキルでは、ソフトウェアサプライチェーンをエンドツーエンドで保護するための検出手法、緊急対応プレイブック、強化戦略を提供します。


1. このスキルを使用する場面

次のいずれかに該当する場合は、このスキルを呼び出してください。

  • 利用している依存関係が侵害されたと報告された場合(例:OSV.dev、GitHub Advisory Database、またはベンダーの開示情報に掲載された勧告)。
  • 依存関係に不審な動作が見られる場合:予期しないネットワーク呼び出し、対象範囲外へのファイルシステム書き込み、新たに追加されたインストール後スクリプトなど。
  • 定期的なサプライチェーンセキュリティ監査を実施している場合。
  • 依存関係の更新後に CI/CD パイプラインが予期しない動作をしている場合。
  • 新しいサードパーティ依存関係を導入するにあたり、その来歴を確認したい場合。
  • タイポスクワッティングされたパッケージやレジストリアカウントの乗っ取りなどのインシデントに対応する必要がある場合。
  • SLSA 準拠に取り組んでいる場合、またはビルド来歴を生成する必要がある場合。

2. 検出

2.1 npm Audit
# Full audit of installed packages
npm audit

# JSON output for programmatic processing
npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical")'

# Fix automatically where possible
npm audit fix

# Check for known malicious packages via Socket.dev CLI
npx socket scan --package-lock package-lock.json
2.2 pip Audit
# Install pip-audit (maintained by Google/OSSF)
pip install pip-audit

# Audit current environment against OSV.dev
pip-audit

# Audit a requirements file directly
pip-audit -r requirements.txt --output json

# Check for typosquatting with bandersnatch or custom script
pip-audit --strict --desc on
2.3 Cargo Audit
# Install cargo-audit
cargo install cargo-audit

# Run audit against RustSec Advisory Database
cargo audit

# JSON output for CI integration
cargo audit --json

# Check for yanked crates
cargo audit --deny yanked
2.4 Sigstore / Cosign の検証
# Verify a container image signature with cosign
cosign verify \
  --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/myimage:latest

# Verify an artifact with sigstore-python
pip install sigstore
python -m sigstore verify identity \
  --cert-identity "[email protected]" \
  --cert-oidc-issuer "https://accounts.google.com" \
  artifact.tar.gz
2.5 SLSA 来歴の確認
# Install slsa-verifier
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest

# Verify provenance of a binary
slsa-verifier verify-artifact my-binary \
  --provenance-path my-binary.intoto.jsonl \
  --source-uri github.com/myorg/myrepo \
  --source-tag v1.2.3

3. 緊急対応プレイブック

依存関係の侵害が確認された場合は、以下の手順を順番に実行してください。

ステップ 1:封じ込め -- バージョンを固定し、更新を停止する
# Pin the last known-good version immediately in package.json
npm install <package>@<safe-version> --save-exact

# For pip, pin with hash verification
pip download <package>==<safe-version> --require-hashes -d ./vendor/

# For cargo, pin in Cargo.toml
# Replace: some_crate = "^1.2" with:
# some_crate = "=1.2.3"
cargo update -p some_crate --precise 1.2.3
ステップ 2:影響範囲を監査する
# Determine which versions you pulled and when
# npm
npm ls <compromised-package>
cat package-lock.json | jq '.packages | to_entries[] | select(.key | contains("<compromised-package>"))'

# pip
pip show <compromised-package>
pip cache list <compromised-package>

# Check git history for when the dependency version changed
git log --all -p -- package-lock.json | grep -A2 -B2 "<compromised-package>"
ステップ 3:侵害の痕跡をスキャンする
# Search for known IOCs from the advisory
grep -r "suspicious-domain.com" ./node_modules/<compromised-package>/
grep -r "eval(atob" ./node_modules/<compromised-package>/

# Check for unexpected post-install scripts
cat node_modules/<compromised-package>/package.json | jq '.scripts'

# For Python packages, inspect setup.py and __init__.py
find ~/.local/lib/python*/site-packages/<compromised-package>/ -name "*.py" \
  | xargs grep -l "subprocess\|os.system\|exec(\|eval("
ステップ 4:関係者に通知する
SUBJECT: [SECURITY INCIDENT] Compromised dependency: <package-name>

SEVERITY: Critical
IMPACT: <package-name> versions <affected-range> contain malicious code.
AFFECTED SYSTEMS: <list of repos/services consuming this dependency>
STATUS: Contained -- pinned to safe version <safe-version>

ACTIONS TAKEN:
1. Pinned all repositories to last known-good version
2. Initiated audit of all systems that pulled affected versions
3. Scanning for indicators of compromise

RECOMMENDED ACTIONS:
- Do NOT deploy any build that consumed affected versions
- Review CI/CD logs for the timeframe <start> to <end>
- Rotate any secrets that were accessible to the build environment
ステップ 5:置き換える、またはフォークする
# If the package maintainer account was compromised, fork the last safe version
git clone https://github.com/original-author/<package>.git
cd <package>
git checkout v<safe-version>
# Publish to your private registry or vendor directly

# For npm, point to your fork via package.json
# "dependencies": { "<package>": "git+https://github.com/yourorg/<package>.git#v1.2.3" }

4. ロックファイルの監査

ロックファイルは最初の防御線です。改ざんされたロックファイルや整合性のないロックファイルは、何か問題があることを示しています。

4.1 ロックファイルの整合性を検証する
# npm: ensure lockfile matches package.json (fails CI if out of sync)
npm ci

# Yarn: check lockfile integrity
yarn install --frozen-lockfile

# pip: generate a hash-locked requirements file
pip-compile --generate-hashes requirements.in -o requirements.txt

# Verify no unexpected changes in lockfile during PR
git diff --name-only origin/main...HEAD | grep -E "(package-lock|yarn.lock|Cargo.lock|requirements.txt)"
4.2 タイポスクワッティングを検出する
# Use the socket CLI to check for typosquatting risk
npx socket scan --package-lock package-lock.json

# Python: check package names against popular packages
pip-audit -r requirements.txt 2>&1 | grep -i "typosquat"

# Custom check: compare package names to known popular packages
# Flag anything with edit distance <= 2 from a top-1000 package
python3 -c "
import json, sys
from difflib import SequenceMatcher
with open('package-lock.json') as f:
    lock = json.load(f)
popular = ['express','lodash','react','axios','chalk','debug','commander','inquirer']
for pkg in lock.get('packages', {}):
    name = pkg.split('node_modules/')[-1] if 'node_modules/' in pkg else pkg
    for p in popular:
        ratio = SequenceMatcher(None, name, p).ratio()
        if 0.75 < ratio < 1.0 and name != p:
            print(f'WARNING: {name} is suspiciously similar to {p} (similarity: {ratio:.2f})')
"
4.3 CI でロックファイルの差分を確認する
# .github/workflows/lockfile-check.yml
name: Lockfile Audit
on: pull_request
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Check for lockfile changes
        run: |
          LOCKFILES="package-lock.json yarn.lock pnpm-lock.yaml Cargo.lock requirements.txt poetry.lock"
          for f in $LOCKFILES; do
            if git diff --name-only origin/main...HEAD | grep -q "$f"; then
              echo "::warning::Lockfile $f was modified -- review dependency changes carefully"
              git diff origin/main...HEAD -- "$f" | head -100
            fi
          done
      - name: Run npm audit
        if: hashFiles('package-lock.json') != ''
        run: npm audit --audit-level=high

5. パッケージのバージョン固定と検証

5.1 pip のハッシュ確認
# requirements.txt with hashes (generated by pip-compile --generate-hashes)
requests==2.31.0 \
    --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb \
    --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7f0edf3fcb0fce8afe0f44546e1
# Install with mandatory hash verification
pip install --require-hashes -r requirements.txt

# Generate hashes for existing requirements
pip-compile --generate-hashes requirements.in
5.2 npm パッケージの整合性確認
# npm automatically verifies integrity hashes in package-lock.json
# Ensure your lockfile contains integrity fields:
cat package-lock.json | jq '.packages | to_entries[] | select(.value.integrity == null) | .key'

# Enable strict engine and audit checks in .npmrc
cat >> .npmrc << 'EOF'
engine-strict=true
audit=true
audit-level=high
EOF
5.3 Rust 用の cargo-vet
# Install cargo-vet
cargo install cargo-vet

# Initialize in your project
cargo vet init

# Certify a crate after review
cargo vet certify serde 1.0.193

# Import audit results from trusted organizations
cargo vet trust --all mozilla
cargo vet trust --all google

# Run verification in CI
cargo vet check

6. コンテナイメージの検証

6.1 Cosign による署名と検証
# Sign an image (keyless via Sigstore/Fulcio in CI)
cosign sign ghcr.io/myorg/myimage@sha256:abc123...

# Verify with expected identity
cosign verify \
  --certificate-identity-regexp "https://github.com/myorg/.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/myimage:latest

# Verify and extract attestations
cosign verify-attestation \
  --type slsaprovenance \
  --certificate-identity-regexp "https://github.com/myorg/.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/myimage:latest | jq '.payload' | base64 -d | jq .
6.2 Kyverno ポリシー -- 署名済みイメージを必須にする
# kyverno-require-signed-images.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: verify-image-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/myorg/*"
                    issuer: "https://token.actions.githubusercontent.com"
                    rekor:
                      url: https://rekor.sigstore.dev
# Apply the policy
kubectl apply -f kyverno-require-signed-images.yaml

# Test: this unsigned image should be rejected
kubectl run test --image=ghcr.io/myorg/unsigned-image:latest
# Expected: admission webhook denies the request

7. CI/CD パイプラインの強化

7.1 GitHub Actions を SHA で固定する
# BAD: mutable tag, can be hijacked
- uses: actions/checkout@v4

# GOOD: pinned to exact commit SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# Use pin-github-action to automate pinning
npm install -g pin-github-action
pin-github-action .github/workflows/*.yml
7.2 分離されたランナー
# Use ephemeral self-hosted runners that are destroyed after each job
jobs:
  build:
    runs-on: self-hosted
    container:
      image: ghcr.io/myorg/build-env:latest@sha256:abc123...
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
      - name: Build in isolated container
        run: |
          # No access to host filesystem or network beyond what's needed
          make build
7.3 クラウド認証に OIDC を使用する(長期間有効なシークレットを使わない)
# GitHub Actions OIDC with AWS -- no static credentials stored
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
          aws-region: us-east-1
      - run: aws s3 cp build/ s3://my-bucket/ --recursive
7.4 ワークフローの権限を制限する
# At the top of every workflow, use least-privilege permissions
permissions:
  contents: read
  packages: read

# Never grant write permissions globally; scope them per job
jobs:
  publish:
    permissions:
      contents: read
      packages: write

8. SLSA フレームワークの導入

8.1 SLSA レベルの概要
レベル要件
SLSA 1ビルドプロセスが文書化され、来歴が生成される
SLSA 2ホスト型ビルドサービスによって来歴が生成され、認証される
SLSA 3ビルドプラットフォームが強化され、来歴の偽造が不可能である
8.2 SLSA レベル 1 -- 来歴を生成する
# .github/workflows/slsa-build.yml
name: SLSA Build
on:
  push:
    tags: ["v*"]
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      digest: ${{ steps.hash.outputs.digest }}
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
      - name: Build artifact
        run: |
          make build
          cp dist/my-binary ./my-binary
      - name: Generate digest
        id: hash
        run: |
          DIGEST=$(sha256sum my-binary | cut -d ' ' -f1)
          echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
      - uses: actions/upload-artifact@v4
        with:
          name: my-binary
          path: my-binary
8.3 SLSA レベル 2-3 -- SLSA GitHub Generator を使用する
  provenance:
    needs: build
    permissions:
      actions: read
      id-token: write
      contents: write
    uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
    with:
      base64-subjects: |
        ${{ needs.build.outputs.digest }} my-binary
      upload-assets: true
8.4 SLSA 来歴を検証する
# Download the provenance and binary from the release
gh release download v1.2.3 -p "my-binary" -p "my-binary.intoto.jsonl"

# Verify
slsa-verifier verify-artifact my-binary \
  --provenance-path my-binary.intoto.jsonl \
  --source-uri github.com/myorg/myrepo \
  --source-tag v1.2.3

echo $?  # 0 = verified successfully

9. 依存関係ファイアウォール

9.1 許可リストを設定した Artifactory リモートリポジトリ
# artifactory-remote-npm.yaml
apiVersion: v1
kind: RemoteRepository
metadata:
  name: npm-remote
spec:
  packageType: npm
  url: https://registry.npmjs.org
  includesPattern: |
    express/**
    lodash/**
    react/**
    @types/**
  excludesPattern: |
    *malicious*
    *typosquat*
  xrayIndex: true
  blockMismatchingMimeTypes: true
  enableTokenAuthentication: true
9.2 Nexus Repository のファイアウォールルール
# Enable Nexus Firewall audit on a proxy repository
curl -u admin:$NEXUS_PASSWORD -X PUT \
  "https://nexus.internal/service/rest/v1/security/content-selectors" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "block-suspicious-pypi",
    "description": "Block packages with no maintainer history",
    "expression": "format == \"pypi\" and coordinate.age < 7"
  }'
9.3 Verdaccio のプライベート npm レジストリ
# verdaccio config.yaml
storage: /verdaccio/storage
uplinks:
  npmjs:
    url: https://registry.npmjs.org/
    cache: true
    maxage: 30m
packages:
  '@myorg/*':
    access: $authenticated
    publish: $authenticated
    proxy: []           # never proxy internal packages
  '**':
    access: $authenticated
    publish: $deny       # block publishing public package names
    proxy: npmjs
  # Block known malicious packages
  'event-stream':
    access: $deny
    publish: $deny

10. 監視とアラート

10.1 プルリクエスト内の新しい依存関係を検出する
# .github/workflows/dependency-review.yml
name: Dependency Review
on: pull_request
permissions:
  contents: read
  pull-requests: write
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
      - uses: actions/dependency-review-action@4901385134134e04cec5fbe5ddfe3b2c5bd5d976 # v4
        with:
          fail-on-severity: high
          deny-licenses: GPL-3.0, AGPL-3.0
          comment-summary-in-pr: always
          warn-only: false
10.2 OSV.dev との統合
# Install osv-scanner
go install github.com/google/osv-scanner/cmd/osv-scanner@latest

# Scan a project directory (auto-detects lockfiles)
osv-scanner -r /path/to/project

# Scan a specific lockfile
osv-scanner --lockfile=package-lock.json

# Scan a Docker image
osv-scanner --docker myimage:latest

# Output as JSON for CI processing
osv-scanner -r /path/to/project --format json | jq '.results[].packages[].vulnerabilities[] | .id'
10.3 Dependabot の設定
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10
    reviewers:
      - "security-team"
    labels:
      - "dependencies"
      - "security"
    # Group minor/patch updates but keep major separate for review
    groups:
      production-dependencies:
        dependency-type: "production"
        update-types: ["minor", "patch"]
      dev-dependencies:
        dependency-type: "development"
        update-types: ["minor", "patch"]

  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "daily"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
10.4 新しい依存関係を通知するカスタム Webhook アラート
#!/usr/bin/env bash
# alert-new-deps.sh -- run in CI on PRs to detect newly added dependencies
set -euo pipefail

BASE_BRANCH="${1:-origin/main}"
LOCKFILE="package-lock.json"

NEW_DEPS=$(diff <(git show "$BASE_BRANCH:$LOCKFILE" 2>/dev/null | jq -r '.packages | keys[]' | sort) \
               <(jq -r '.packages | keys[]' "$LOCKFILE" | sort) \
           | grep "^>" | sed 's/^> //' || true)

if [ -n "$NEW_DEPS" ]; then
  echo "New dependencies detected:"
  echo "$NEW_DEPS"

  # Send to Slack
  curl -s -X POST "$SLACK_WEBHOOK_URL" \
    -H "Content-Type: application/json" \
    -d "{
      \"text\": \"New dependencies added in PR #${PR_NUMBER}:\n\`\`\`${NEW_DEPS}\`\`\`\",
      \"channel\": \"#security-alerts\"
    }"
fi

11. インシデント発生後の対応

11.1 フォレンジックのチェックリスト
[ ] Identify the exact compromised package version(s)
[ ] Determine the time window of exposure (first install to detection)
[ ] List all repositories and services that consumed the package
[ ] Check CI/CD build logs for the exposure window
[ ] Inspect runtime logs for outbound connections to unknown hosts
[ ] Review process execution logs for unexpected child processes
[ ] Check for modifications to other files in node_modules/site-packages
[ ] Verify no additional packages were installed as transitive deps
[ ] Dump and analyze DNS query logs for the exposure period
[ ] Check for new cron jobs, systemd services, or scheduled tasks
[ ] Audit all secrets/tokens that were accessible to the build environment
11.2 影響範囲の評価
#!/usr/bin/env bash
# blast-radius.sh -- assess how widely a compromised package spread
set -euo pipefail

COMPROMISED_PKG="$1"
COMPROMISED_VERSIONS="$2"  # comma-separated, e.g., "1.2.3,1.2.4"

echo "=== Blast Radius Assessment for $COMPROMISED_PKG ==="

# Check all repos in the org
for repo in $(gh repo list myorg --json name -q '.[].name'); do
  echo "--- Checking $repo ---"

  # Check package-lock.json
  LOCK=$(gh api "repos/myorg/$repo/contents/package-lock.json" \
    --jq '.content' 2>/dev/null | base64 -d 2>/dev/null || true)

  if echo "$LOCK" | grep -q "\"$COMPROMISED_PKG\""; then
    VERSION=$(echo "$LOCK" | jq -r ".packages[\"node_modules/$COMPROMISED_PKG\"].version // empty")
    if echo "$COMPROMISED_VERSIONS" | grep -q "$VERSION"; then
      echo "AFFECTED: $repo uses $COMPROMISED_PKG@$VERSION"
    fi
  fi
done
11.3 侵害後のシークレットのローテーション
# Rotate all secrets that were accessible during the exposure window

# 1. Rotate cloud provider credentials
aws iam create-access-key --user-name ci-deploy
aws iam delete-access-key --user-name ci-deploy --access-key-id OLD_KEY_ID

# 2. Rotate GitHub tokens
gh auth refresh

# 3. Rotate database credentials
kubectl create secret generic db-credentials \
  --from-literal=password="$(openssl rand -base64 32)" \
  --dry-run=client -o yaml | kubectl apply -f -

# 4. Rotate npm/PyPI publish tokens
npm token revoke <old-token>
npm token create --read-only

# 5. Invalidate all active sessions/JWTs
# Application-specific -- trigger a key rotation in your auth service
11.4 コミュニケーション用テンプレート
--- INTERNAL INCIDENT REPORT ---

Incident ID: SC-YYYY-NNN
Date Detected: YYYY-MM-DD HH:MM UTC
Package: <name>@<version>
Registry: npm / PyPI / crates.io
Advisory: <link to CVE or advisory>

Timeline:
  - YYYY-MM-DD HH:MM: Compromised version published to registry
  - YYYY-MM-DD HH:MM: First installation in our environment (from CI logs)
  - YYYY-MM-DD HH:MM: Compromise detected via <audit tool / advisory / manual review>
  - YYYY-MM-DD HH:MM: Pinned to safe version across all repos
  - YYYY-MM-DD HH:MM: Completed IOC scan -- no evidence of exploitation
  - YYYY-MM-DD HH:MM: All exposed secrets rotated

Blast Radius:
  - Repositories affected: N
  - Production deployments with compromised version: N
  - Secrets potentially exposed: <list>

Root Cause:
  <Maintainer account takeover / malicious maintainer / build system compromise>

Remediation:
  1. Pinned to safe version
  2. Rotated all potentially exposed secrets
  3. Deployed clean builds to production
  4. Added package to monitoring watch list

Preventive Measures:
  1. Enabled hash-pinning for all dependencies
  2. Added dependency-review-action to all repos
  3. Configured Artifactory proxy with allowlist
  4. Scheduled quarterly supply chain audits

クイックリファレンス

タスクコマンド
npm を監査npm audit --json
pip を監査pip-audit -r requirements.txt
cargo を監査cargo audit
OSV でスキャンosv-scanner -r .
cosign 署名を検証cosign verify --certificate-identity-regexp ... <image>
SLSA 来歴を検証slsa-verifier verify-artifact ...
GitHub Actions を固定pin-github-action .github/workflows/*.yml
ロックファイルの差分を確認npm ci(ロックファイルに不整合がある場合は失敗します)
pip のハッシュを生成pip-compile --generate-hashes requirements.in
Cargo vet のチェックcargo vet check

コメント