
開発者PCのサプライチェーン攻撃対策を実践してみた
はじめに
サプライチェーン攻撃とは、開発者のPCやツールを経由して悪意あるコードを忍び込ませる攻撃手法です。認証情報の窃取、悪意ある拡張機能、パッケージレジストリへの偽パッケージ混入など、開発者を狙ったベクタが増えています。
なぜ今、対策が必要か
「セキュリティ対策は重要」とは分かっていても、どこか対岸の話に感じることがあります。しかし 2026 年に入ってから、誰でも知っているオープンソースパッケージが次々と侵害されています。
サプライチェーン攻撃: LiteLLM(2026年3月)
AI 開発フレームワーク LiteLLM は、CI/CD パイプラインでセキュリティスキャナー「Trivy」を使っていました。攻撃者グループ TeamPCP はまず Trivy を侵害し、LiteLLM の GitHub Actions 上にある PyPI 公開トークンを窃取。2026 年 3 月 24 日、悪意あるバージョン(1.82.7、1.82.8)を PyPI に直接公開しました(CVE-2026-33634、CVSS 9.4)。
月間 9,500 万ダウンロードのパッケージへの攻撃であり、「セキュリティスキャナー自体が攻撃の入口になった」 という点が最も皮肉です。CI/CD が信頼していたツールこそが、クレデンシャル窃取の踏み台になりました。
サプライチェーン攻撃: Axios(2026年3月)
週間 7,000 万ダウンロードを誇る JavaScript HTTP クライアント Axios は、メンテナーの npm アカウントが北朝鮮系脅威アクター(Sapphire Sleet)に侵害されました。わずか 39 分の窓で悪意あるバージョン(1.14.1、0.30.4)が公開され、macOS・Windows・Linux に対応したクロスプラットフォーム RAT が仕込まれていました(CISA 公式アドバイザリ)。
ゼロデイ脆弱性: React Server Components(CVE-2025-55182、CVSS 10.0)
サプライチェーン攻撃とは異なり、ゼロデイ攻撃は正規のソフトウェア自体に潜む未知の脆弱性を突きます。2025 年 12 月に発見された React Server Components の脆弱性(CVE-2025-55182)は CVSS スコア 10.0(最高値)を記録し、Next.js 14.3 以降・React 19 系に影響しました。ワークアラウンドなしでパッチ適用のみが対策であり、パッケージ更新を長期間ブロックする設定にしていた環境は、この緊急パッチも適用できなかった可能性があります。これが、本記事の対策⑦で 7 日間という設定を採用した根拠のひとつです。
これらの共通点は「攻撃者は最も信頼されているコンポーネントを狙う」ということです。自分がコードを書いていなくても、依存するパッケージや CI/CD ツールを通じて被害を受けます。以下では、開発者 PC レベルでできる 7 つの対策を紹介します。

環境
- macOS Sequoia
- シェル: zsh
- エディタ: VSCode
- パッケージマネージャー: pnpm (Node.js), uv (Python)
- AWS認証: aws-vault
- パスワードマネージャー: 1Password
対策①: VSCode拡張機能の自動アップデートを無効化する
VSCode はデフォルトで拡張機能を自動更新します。拡張機能が乗っ取られたり、マルウェアが混入した更新が配信された場合に、自動更新が最短経路になってしまいます。
settings.json に以下を追記します。
{
"extensions.autoUpdate": false
}
設定後は、更新前に変更内容を確認してから手動でアップデートするフローに切り替わります。
対策②: SSHキーにパスフレーズを設定し、macOS Keychainに登録する
SSHキーが無パスフレーズだと、PCが盗難・紛失した際に即座に使われてしまいます。パスフレーズを設定した上で、macOS Keychainに登録することで、毎回入力する手間なく保護できます。
# 既存キーにパスフレーズを設定
ssh-keygen -p -f ~/.ssh/id_ed25519
# Keychainに登録(以降は自動で読み込まれる)
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
~/.ssh/config に以下を追記しておくと、新しいターミナルを開くたびに自動でエージェントに追加されます。
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
この設定により、Node.js の ssh2 ライブラリなど SSH エージェントを参照するツールでも、パスフレーズの再入力なしにキーを使えるようになります。
対策③: AWSクレデンシャルをaws-vaultに移行する
~/.aws/credentials にアクセスキーIDとシークレットを平文で保存している場合、ファイルを覗かれるだけで認証情報が漏洩します。aws-vault を使うと、クレデンシャルを macOS Keychain に保存し、コマンド実行時だけ一時的な認証情報を発行できます。
# インストール
brew install aws-vault
# クレデンシャルを登録(対話式でKeychain保存)
aws-vault add <profile-name>
# 動作確認
aws-vault exec <profile-name> -- aws sts get-caller-identity
移行後は ~/.aws/credentials から平文キーを削除できます。boto3 などのSDKは aws-vault exec 経由で起動することで、環境変数として一時クレデンシャルを受け取ります。
対策④: GitHub CLIをFine-grained PATで認証する
Classic PAT は権限が広くなりがちです。Fine-grained PAT はリソースオーナー(個人アカウントまたは組織)ごとに発行し、リポジトリ単位・操作単位で権限を絞れます。
GitHub の設定画面から Fine-grained PAT を発行します。
- Settings → Developer settings → Personal access tokens → Fine-grained tokens
- 必要な権限:
Contents(読み書き)、Pull requests(読み書き)、Workflows(読み書き)、Metadata(読み取り)
# 発行したトークンでログイン
gh auth login --with-token <<< "github_pat_xxxx"
# 確認 — (keyring) と github_pat_ が表示されればOK
gh auth status
トークンは macOS Keychain に保存されるため、平文ファイルには残りません。
対策⑤: .env ファイルのシークレットを1Passwordに移行する
プロジェクトの .env ファイルに API キーを書いている場合、誤ってコミットしたり、マルウェアにスキャンされるリスクがあります。1Password CLI (op) を使うと、シークレットをボルトに保存し、コマンド実行時だけプロセスに注入できます。
シークレットを1Passwordに登録する
op item create \
--vault Employee \
--category "API Credential" \
--title "Contentful CMA Token" \
"token[password]=CFPAT-xxxx"
.env.1p テンプレートを作成する
.env ファイルを削除し、代わりに op:// 参照を使ったテンプレートを作成します。
# .env.1p
CONTENTFUL_CMA_TOKEN=op://Employee/Contentful CMA Token/token
このファイルには実際のシークレットが含まれないため、リポジトリにコミットしても安全です。
op run でコマンドを実行する
op run --env-file .env.1p -- pnpm dev
op run --env-file .env.1p -- node script.js
op run が op:// 参照を実際の値に置き換えてからプロセスを起動します。シークレットはディスクに書き込まれません。
1Password CLIのデスクトップアプリ統合
1Password アプリと CLI を連携させると、ターミナルを開くたびに Touch ID 一回でセッションが確立されます。
1Password → 設定 → Developer → 「1Password CLIと連携する」を有効化

対策⑥: npm/npx/pip/pip3 を禁止して pnpm/uv に統一する
npx はネットワーク越しにパッケージを実行できるため、タイポスクワッティング攻撃(typosquatting)の格好の標的です。pip も同様にリスクがあります。
.zshrc にエイリアスを追加して使用をブロックします。
alias npm='echo "npm is blocked. Use pnpm instead: pnpm install / pnpm add / pnpm run" >&2 && false'
alias npx='echo "npx is blocked. Use pnpm dlx instead: pnpm dlx <package>" >&2 && false'
alias pip='echo "pip is blocked. Use uv instead: uv add / uv run / uv sync" >&2 && false'
alias pip3='echo "pip3 is blocked. Use uv instead: uv add / uv run / uv sync" >&2 && false'
エラーメッセージを英語にしておくと、AI コーディングアシスタントが代替コマンドを正しく把握して自己修正できます。
対策⑦: 新しすぎるパッケージのインストールを制限する
新しく公開されたパッケージは、セキュリティコミュニティによるチェックが入る前の期間が最も危険です。
なぜ 7 日間か
PyPI の 2025 年セキュリティレポートによると、悪意あるパッケージの 66% は公開後 4 時間以内に検出・削除されています。残りの大半も 72 時間(3 日間) 以内に対応されており、7 日間待つことで実用上ほぼすべてのタイポスクワッティングパッケージをカバーできます。
一方、制限を長くしすぎると別のリスクが生まれます。たとえば 3 週間に設定した場合、既知の CVE に対するセキュリティパッチも同じ期間ブロックされます。「サプライチェーン攻撃を防ぐために、既知の脆弱性を 3 週間放置する」という皮肉な状況になりかねません。
7 日間はこのトレードオフの落とし所として、uv の公式ドキュメントや pnpm のセキュリティブログでも採用されている設定です。
uv の設定
# ~/.config/uv/uv.toml
exclude-newer = "7 days"
正規パッケージの緊急パッチを適用したい場合は、コマンド単位で上書きできます。
uv add some-package --exclude-newer 2099-12-31 # 制限を実質無効化
あわせて uv 自体のバージョンも確認します。CVE-2025-54368 は v0.8.5 以下に影響するため、最新版にアップデートしておきます。
uv self update
pnpm の設定
pnpm 10.16 以降では minimumReleaseAge という同等の設定が使えます。~/.npmrc に追記するだけで全プロジェクトに適用されます。
# ~/.npmrc
minimum-release-age=7 days
緊急パッチが必要な特定パッケージは minimumReleaseAgeExclude で除外できます。
# ~/.npmrc
minimum-release-age=7 days
minimum-release-age-exclude=react,react-dom
まとめ
今回実施した対策をまとめると以下の通りです。
| 対策 | 変更前 | 変更後 |
|---|---|---|
| VSCode拡張機能 | 自動更新ON | 自動更新OFF(手動確認) |
| SSHキー | パスフレーズなし | パスフレーズ + Keychain登録 |
| AWSクレデンシャル | ~/.aws/credentials に平文 |
aws-vault経由(Keychain保存) |
| GitHub CLIトークン | Classic PAT / OAuth | Fine-grained PAT(Keychain保存) |
| プロジェクトシークレット | .env ファイルに平文 |
1Password保存 + op run で注入 |
| npm/npx | 使用可 | ブロック(pnpm/pnpm dlx へ) |
| pip/pip3 | 使用可 | ブロック(uv へ) |
| uv パッケージ取得 | 制限なし | 7日以内公開パッケージを除外 |
| pnpm パッケージ取得 | 制限なし | 7日以内公開パッケージを除外 |
チェックスクリプトを実行したときは複数の ✗ が並んでいましたが、対策後はすべて ✓ になりました。どれも数分〜数十分で完了できる対策ばかりです。
サプライチェーン攻撃はゼロリスクにはできませんが、認証情報の平文保存をなくし、使用するツールを絞り込むだけで、攻撃の難易度を大幅に引き上げることができます。定期的にチェックスクリプトを実行して、設定の劣化を防ぎましょう。






