ソフトウェアサプライチェーンセキュリティ入門|SBOM・Dependabot・ロックファイルで依存関係を守る

kento_morota 13分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

自社で書いたコードは数千行でも、npm installpip installで取り込んでいるコードは数十万行、という状態は珍しくありません。その大半は自分で読んだことのない他人のコードです。「有名なパッケージだから大丈夫」と思っていたら、ある日そのパッケージのメンテナが乗っ取られ、認証情報を盗むコードが混入していた——こうした事件は、ここ数年で現実に繰り返し起きています。

この記事では、依存パッケージ経由の攻撃にどんな型があるかを整理し、ロックファイルとバージョン固定、npm audit・pip-auditによる脆弱性検出、Dependabot・Renovateによる更新の自動化、SyftによるSBOM生成とTrivyによるスキャン、そしてそれらをCIに組み込む方法を解説します。自社アプリの脆弱性そのものはOWASP Top 10、コンテナイメージの安全性はDockerセキュリティのベストプラクティスで扱っているので、ここでは「取り込んでいる他人のコード」に焦点を絞ります。

ソフトウェアサプライチェーン攻撃とは|依存パッケージ経由の攻撃の型

「サプライチェーン」は部品の調達経路

製造業で部品の調達経路をサプライチェーンと呼ぶように、ソフトウェアでも「自社コード+ライブラリ+ビルドツール+コンテナベースイメージ+CI環境」という調達経路全体を指してソフトウェアサプライチェーンと呼びます。攻撃者にとっては、堅牢な自社アプリを直接攻めるより、そのアプリが信頼して取り込む部品に細工するほうが効率的です。1つの人気パッケージに悪意あるコードを混ぜられれば、それを使う何万ものプロジェクトに一度に侵入できるからです。

代表的な攻撃の型

攻撃の型手口主な防御
タイポスクワッティングlodashに対してlodahsのような、打ち間違いを狙った名前で悪意あるパッケージを公開するロックファイル、パッケージ名のレビュー、社内ミラー
依存関係の混乱(Dependency Confusion)社内限定パッケージと同名のパッケージを公開レジストリに高いバージョンで置き、公開側が優先されるようにするレジストリのスコープ固定、社内パッケージ名の予約
メンテナ乗っ取り・引き継ぎ長年更新されていないパッケージの管理権を譲り受けたり、アカウントを乗っ取ったりして悪意あるバージョンを公開するバージョン固定、更新時の差分確認、更新の遅延導入
既知の脆弱性の放置攻撃ではなく怠慢だが、公開済みの脆弱性を持つ古いバージョンを使い続けるnpm audit、Dependabot、SBOMによる継続監視
ビルド・CI環境の侵害CIのアクションやビルドスクリプトを改ざんし、ビルド成果物に細工するアクションのバージョンをハッシュで固定、権限の最小化

これらに共通するのは、「いつの間にか取り込んでいたものが変わっていた」という点です。したがって防御の基本は、何を取り込んでいるかを正確に把握し(可視化)、意図しない変化を止め(固定)、既知の問題を継続的に検出する(監視)の3つになります。

ロックファイルとバージョン固定|「意図しない変化」を止める

ロックファイルが守っているもの

package.json"express": "^4.18.0"という指定は「4.18.0以上5.0未満のどれか」という意味で、インストールするタイミングによって実際のバージョンが変わります。これを「解決した結果」として、間接的な依存まで含めて厳密なバージョンと内容のハッシュを記録するのがpackage-lock.json(npm)、yarn.lockpoetry.lockGemfile.lockといったロックファイルです。ロックファイルがあれば、開発者のPCでもCIでも本番でも、まったく同じ内容が展開されます。

実務での必須ルールは次の3つです。

  1. ロックファイルを必ずGitにコミットする(アプリケーションの場合)。
  2. CIと本番ではnpm installではなくnpm ciを使う。npm ciはロックファイルとpackage.jsonが矛盾するとエラーになり、勝手にロックファイルを書き換えません。
  3. ロックファイルの差分をコードレビューの対象にする。見慣れないパッケージが増えていたら、その理由を確認する。
# CIや本番ではロックファイルどおりに再現インストールする
npm ci

# Pythonの場合:ハッシュ付きで固定した要件ファイルから入れる
pip install --require-hashes -r requirements.txt

# 依存の中身が「登録時と同じか」を検証する(npm)
npm ci --ignore-scripts   # インストール時スクリプトの実行を抑止し、悪性コードの実行機会を減らす

パッケージ管理ツールの基本的な動作はパッケージ管理ツールの仕組みと選び方で解説しています。

「最新すぐ導入」をあえてしない

メンテナ乗っ取り型の攻撃では、悪意あるバージョンが公開されてから発覚・取り下げまで数時間〜数日かかります。この間に自動更新で取り込んでしまうのが最悪のパターンです。対策として、「公開から一定日数(たとえば3〜7日)経ったバージョンだけを更新候補にする」運用が有効で、RenovateにはminimumReleaseAgeという設定があります。セキュリティ修正の即時適用とのバランスは、脆弱性アラートは即時、通常更新は遅延、と使い分けます。

npm audit・pip-auditとDependabot・Renovateの運用

既知の脆弱性を検出する

npm auditpip-auditは、ロックファイルに記録された依存関係を脆弱性データベースと突き合わせ、既知の脆弱性を持つバージョンを報告します。手元で実行するだけでなく、CIで「重大度High以上があればビルドを失敗させる」ようにするのが基本形です。

# Node.js:重大度high以上があれば非ゼロ終了(CIで失敗させる)
npm audit --audit-level=high

# Python:pip-auditで環境またはrequirementsを検査
pip install pip-audit
pip-audit -r requirements.txt

# 自動修正(ロックファイルの範囲内で安全なバージョンに上げる)
npm audit fix

注意点として、npm auditの結果には「開発時にしか使わないツールの、実際には到達しない脆弱性」も多く含まれます。すべてをゼロにするのではなく、本番で動くコードに含まれる(--omit=devで絞る)High以上を優先し、それ以外は期限を決めて対応する、という優先順位づけが必要です。

Dependabot・Renovateで更新を自動化する

脆弱性が見つかってから手動で更新していると、必ず遅れが出ます。GitHubのDependabotやRenovateは、依存関係の更新をプルリクエストとして自動作成し、CIでテストを通したうえでレビューに回してくれます。

# .github/dependabot.yml の最小例
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
    groups:
      minor-and-patch:
        update-types: ["minor", "patch"]   # マイナー・パッチはまとめて1PRに
  - package-ecosystem: "github-actions"   # CIで使うアクション自体も更新対象にする
    directory: "/"
    schedule:
      interval: "weekly"
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"

運用のコツは、「PRが溜まって誰も見なくなる」状態を避けることです。マイナー・パッチ更新はグループ化して週1回まとめてマージし、メジャー更新だけ個別に検討する。セキュリティアラート由来のPRは優先ラベルを付けて当日中に確認する。この2段構えにすると、中小規模のチームでも無理なく回せます。GitHub Actions側の設定の基本はGitHub Actions入門を参照してください。

SBOM生成(Syft)とTrivyスキャン|「何を使っているか」を証明する

SBOMは部品表

SBOM(Software Bill of Materials)は、ソフトウェアに含まれるすべてのコンポーネントとバージョンを列挙した「部品表」です。新しい脆弱性が公表されたとき、「うちのどのシステムがそのライブラリを使っているか」を即座に答えられることが目的です。SBOMがなければ、全リポジトリを手作業でgrepして回ることになります。取引先や監査からSBOMの提出を求められるケースも増えており、フォーマットはSPDXとCycloneDXの2つが標準的です。

SyftはSBOMを生成するツールで、ソースディレクトリやコンテナイメージを対象にできます。Trivyは脆弱性スキャナーで、SBOMやイメージを入力に既知の脆弱性を検出します。

# SyftでコンテナイメージからCycloneDX形式のSBOMを生成
syft myapp:1.4.2 -o cyclonedx-json > sbom.cdx.json

# 生成したSBOMをTrivyでスキャンし、重大度HIGH以上があれば失敗させる
trivy sbom sbom.cdx.json --severity HIGH,CRITICAL --exit-code 1

# イメージを直接スキャンすることもできる(OSパッケージとアプリ依存の両方を検査)
trivy image --severity HIGH,CRITICAL myapp:1.4.2

コンテナイメージを対象にすると、npmやpipの依存だけでなく、ベースイメージのOSパッケージ(OpenSSLやglibcなど)の脆弱性も同時に検出できます。アプリの依存は綺麗でもベースイメージが古い、というケースは非常に多いものです。

SBOMは成果物と一緒に保管する

SBOMはビルドのたびに生成し、リリースされたバージョンに紐づけて保管します。「本番で動いている1.4.2のSBOM」がすぐ取り出せる状態にしておけば、脆弱性公表時の影響調査が数分で終わります。GitHub Releasesに添付する、あるいはS3などのストレージにタグ名で保存するのが簡単な方法です。

CIでの自動化と運用の落とし穴

ワークフローに組み込む

# .github/workflows/supply-chain.yml(抜粋)
name: supply-chain
on: [pull_request, push]
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
      - run: npm ci --ignore-scripts
      - run: npm audit --audit-level=high --omit=dev
      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .
      - name: SBOM and scan
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: myapp:${{ github.sha }}
          severity: HIGH,CRITICAL
          exit-code: 1

ここでuses:に指定しているアクション自体もサプライチェーンの一部です。より厳密にするなら、タグ(@v4)ではなくコミットハッシュで固定し、Dependabotのgithub-actionsエコシステムで更新を追う、という形にします。

トラブル事例:ロックファイルがあるのに本番だけ挙動が違う

症状:ステージングでは動くのに、本番デプロイ後だけ日付処理が壊れた。package-lock.jsonはコミットされていた。

原因:本番のデプロイスクリプトがnpm install --productionを使っていた。開発者がローカルでpackage.jsonだけ編集してロックファイルを更新せずにコミットしたため、npm installが本番で新しい解決を行い、間接依存の日付ライブラリがメジャーバージョンアップしていた。ステージングは古いキャッシュを使っていて差が出なかった。

対処:本番・ステージングともnpm ci --omit=devに統一し、CIに「package.jsonとロックファイルの矛盾があれば失敗」というチェック(npm ci自体がこれを兼ねる)を追加。さらにロックファイルの変更を含むPRには必ずレビュアーを付けるルールにした。

もうひとつの落とし穴が、CIに渡す認証情報です。レジストリのトークンや署名鍵がリポジトリに紛れ込むと、サプライチェーンの防御そのものが崩れます。秘密情報をGitに入れない仕組みとあわせて整備してください。

まとめ

ソフトウェアサプライチェーンの防御は、可視化(SBOM)・固定(ロックファイルとnpm ci)・監視(audit、Dependabot、Trivy)の3本柱で成り立ちます。タイポスクワッティングやメンテナ乗っ取りは「いつの間にか変わっていた」ことを突く攻撃なので、変化を止め、変化があれば気づける仕組みが最大の防御です。まずはロックファイルのコミットとnpm ciの徹底、次にDependabotの有効化、そしてCIでのTrivyスキャンとSBOM保管、という順に導入すれば、数日で一通りの体制が整います。

依存関係の棚卸しからCIパイプラインへの組み込み、SBOM提出への対応まで、実装面での支援が必要な場合はHarmonic Societyのシステム開発・インフラ支援にご相談ください。

#サプライチェーン#SBOM#Dependabot#依存関係

Harmonic Society

この記事の内容、自社の業務でも活かせそうですか?

ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。

共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

Harmonic Society

「読んで終わり」にせず、自社の業務で試してみませんか?

AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。

無料・30分・オンラインOK|1営業日以内に返信します