
はじめに
こんにちは、株式会社ACES の共同創業者 / ソフトウェアエンジニアの三田村です。
2024〜2026年にかけて、OSS パッケージや CI/CD を経由したソフトウェアサプライチェーン攻撃が増加しています。当社は Python と JavaScript/TypeScript を主な開発言語として使用し、CI/CD パイプラインやクラウドサービスに依存しているため、以前からこの問題を認識していました。
直接的なきっかけとなったのは、2026年初頭に発生した Trivy(セキュリティスキャナ)の侵害と axios(HTTP クライアントライブラリ)のメンテナー権限悪用の 2 件です。Trivy の件はセキュリティツール自体が攻撃対象になりうることを示し、axios の件は広く利用されている有名パッケージであっても依存関係の改変リスクがあることを示しました。いずれも当社の開発環境に直接関係するツール・ライブラリであったため、個別対応ではなく全社のガイドラインとして整備することにしました。
本記事では、その内容を紹介します。同程度の規模・技術スタックの開発組織にとって、参考になる部分があれば幸いです。
背景:攻撃手法の変化
従来のサプライチェーン攻撃は、タイポスクワッティング(名前を似せた悪意あるパッケージの登録)が主でした。近年は、信頼済みの経路そのものを利用した攻撃が増えています。
- OSS メンテナーのアカウント乗っ取りによる正規パッケージの改変
- GitHub Actions のタグ書き換え(tag poisoning)
postinstall/preinstallを経由した不正コード実行- CI トークン等を起点としたワーム型の横展開
- セキュリティスキャナ自体の侵害
近年のインシデントから得た教訓
- xz-utils(2024年): 数年にわたるソーシャルエンジニアリングで上流コンポーネントにバックドアが混入された。長期間利用されている OSS であっても安全とは限らない。
- tj-actions/changed-files(2025年): GitHub Actions のタグが書き換えられ、利用者が意図せず改変済みコードを実行した。
@v1等の可変タグ指定にはリスクがある。 - npm/PyPI のワーム型攻撃(2025〜2026年): CI の秘密情報を起点に自己増殖的に感染が拡大した。単一のトークン漏洩がリポジトリ横断の問題に発展しうる。
- セキュリティツールの侵害(2026年): セキュリティツール自体が攻撃対象になり、スキャン処理中に認証情報が窃取された。検査ツールもゼロトラストで扱う必要がある。
標準化した 8 つのルール
これらの状況を踏まえ、以下を全社の標準ルールとして導入しました。
1. 依存関係のゼロトラスト
利用実績の長さや知名度に関わらず、すべての外部依存を侵害されうるものとして扱います。
2. ロックファイルの必須化
以下のロックファイルをリポジトリにコミットし、CI ではロックファイルに基づくインストールのみ許可しています。
uv.lock(uv)poetry.lock(Poetry)package-lock.json(npm)yarn.lock(Yarn)
CI での実行コマンドは以下の通りです。
# Python (uv) uv sync --locked # JavaScript/TypeScript (npm) npm ci --ignore-scripts # JavaScript/TypeScript (Yarn Berry) yarn install --immutable
3. インストールスクリプトの原則無効化
npm / Yarn のライフサイクルスクリプトは既定で無効としています。
postinstall/preinstallを無効化- ネイティブ拡張等で必要なパッケージのみ個別に許可
設定例:
# .npmrc ignore-scripts=true save-exact=true
# .yarnrc.yml (Yarn Berry) enableScripts: false
4. GitHub Actions の SHA 固定
第三者アクションは可変タグではなくフルコミット SHA で固定しています。
@v1/@v3等のタグ指定は禁止- フルコミット SHA にピン留め
# NG - uses: some-org/some-action@v1 # OK - uses: some-org/some-action@a3fcfb04bbe59c91f973cb6447e1bb1523abcb0b
既存ワークフローは pinact で一括変換し、CI に漏れ検知ステップを追加しています。SHA 固定後のバージョン追従は Dependabot で管理しています。
5. CI/CD 権限の最小化
GITHUB_TOKEN は既定で読み取り専用とし、必要なジョブにのみ最小限の権限を付与しています。本番環境へのアクセスは OIDC ベースへの移行を進めています。
permissions: contents: read jobs: deploy: permissions: contents: read id-token: write # OIDC
6. 検疫期間の設置
新規リリースのパッケージを即時反映せず、48〜72 時間のクールダウン期間を設けています。
- Python:
uvのexclude-newerを利用。v0.9.17 以降、"3 days"のような相対指定にも対応している - JavaScript/TypeScript: Renovate の
minimumReleaseAgeで同等の運用
設定例:
# Python (uv) - pyproject.toml [tool.uv] exclude-newer = "3 days"
// JavaScript/TypeScript (Renovate) - renovate.json { "minimumReleaseAge": "3 days" }
7. SBOM / 脆弱性スキャンの標準化
全リリースで SBOM を生成し、PR・main・release の各段階で依存関係スキャンを実施する方針です。脆弱性管理ツールの選定は現在進行中です。
8. 新規依存関係の導入ルール
新しいパッケージの導入時は、以下を確認するプロセスを設けています。
- 保守状況・メンテナー体制
- 代替可能性
- ライセンス
- install script の有無
- 認証・暗号・通信に関わるライブラリかどうか
上記のうち、特にリスクの高い条件に該当する場合は追加の精査を行います。
技術スタック別の対応
Python
新規プロジェクトでは uv を第一候補としています。exclude-newer による検疫や、locked / frozen オプションによる厳格なインストール制御が利用できます。既存の Poetry プロジェクトは即時移行を求めず、いずれの場合もロックファイルと監査の運用を必須としています。
JavaScript/TypeScript
インストール時のライフサイクルスクリプトが主なリスクです。
- 既定でスクリプトを無効化
- 必要なパッケージのみ allowlist 方式で許可
GitHub Actions / CI/CD
SHA ピン留め、最小権限の permissions 設定、build/test とデプロイの分離を基本方針としています。pull_request_target の利用は原則禁止です。
インシデント対応
対策を講じても侵害を完全に防ぐことはできないため、発生時の対応フローも整備しています。
検知・トリアージ(0〜2 時間)→ 封じ込め(2〜24 時間)→ 根絶(24〜72 時間)→ 復旧 → 事後分析の各フェーズで対応内容を定義しています。サプライチェーン攻撃では CVE の有無よりも悪性挙動の有無を判断基準としています。
おわりに
サプライチェーンセキュリティは特定のツール導入で完了するものではなく、開発プロセス全体の運用を継続的に見直す取り組みです。当社でも脆弱性管理ツールの選定やプライベートレジストリの導入など、未着手の項目が残っています。
同様の課題に取り組んでいる開発組織の参考になれば幸いです。