关于 detecting-dependency-confusion
面向供应链安全和构建工程团队,指导检测并防范依赖混淆风险。它结合依赖清单扫描、公共注册表核查、私有源固定和 CI 检查,覆盖持续发现与修复。
主要功能
- 01扫描多种生态的依赖清单
- 02识别公共注册表中的可占用名称
- 03交叉核查依赖风险
- 04固定私有注册表来源
- 05为未占用名称注册占位包
- 06在 CI 中持续检测
适用场景
- 01为代码库建立依赖混淆风险基线
- 02审查同时使用公有和私有源的流水线
- 03调查内部包名称泄露后的风险
- 04在 CI 中检测新增的可混淆依赖
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill detecting-dependency-confusion安装后在 Claude Code 中输入/detecting-dependency-confusion确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdstandards.mdscripts/agent.py详细介绍
检测依赖混淆
法律声明: 本技能仅用于经授权的安全测试、防御性工程和教育目的。注册或认领你不拥有的包命名空间,或在没有书面授权的情况下测试构建流水线,可能违法,并可能违反公共仓库的服务条款。仅对你控制或明确获得授权评估的名称和基础设施执行命名空间认领或解析测试活动。
概述
依赖混淆(也称为替换攻击或命名空间遮蔽攻击)由 Alex Birsan 在 2021 年推向公众视野,当时他强制将恶意代码注入 Apple、Microsoft、PayPal 等数十家公司的内部构建系统。根本原因在于,许多包管理器在配置为同时从内部/私有仓库和公共仓库解析时,会优先选择版本号更高的副本,而不是遵循来源。攻击者一旦得知私有包的名称(@acme/internal-utils、acme-billing-sdk),就可以向公共仓库(npmjs.com、PyPI、Maven Central)发布一个同名的恶意包,并使用极高的版本号(例如 99.0.0)。当受害者的 CI/CD runner 或开发机解析依赖时,它会拉取攻击者的公共包,执行其安装脚本,供应链即被攻陷。
本技能涵盖问题的两个方面:检测——使用 confused 和 OWASP dep-scan 枚举未在公共仓库注册(即未被防御性占位)因而可被认领的内部包名称——以及预防——将作用域/命名空间固定到私有仓库、注册占位包,并在 .npmrc、pip.conf/pyproject.toml 和 Maven settings.xml 中强制实施来源限制。内部包名称不断泄露:在提交的锁文件、sourcemap、公开 JS bundle、Docker 层和错误堆栈跟踪中,因此这被视为攻击面管理问题,而不是一次性检查。
何时使用
- 在将仓库或组织纳入供应链安全计划时,需要为哪些内部包可在公共仓库被认领建立基线。
- 当 CI/CD 流水线同时从私有仓库和公共仓库解析依赖时(混合/混用 feed)。
- 在任何可能已暴露内部包名称的事件之后(源码泄露、公开 bundle、仓库被入侵)。
- 在审计
package.json、requirements.txt、pom.xml、composer.json或Gemfile.lock文件中的可混淆依赖时。 - 作为定期计划性控制措施,检测新添加但尚未被防御性注册的内部包。
前置条件
- Go 1.20+ 用于安装
confused:go install github.com/visma-prodsec/confused@latest # binary lands in $(go env GOPATH)/bin/confused - Python 3.10+ 用于 OWASP dep-scan:
pip install owasp-depscan # or container: docker pull ghcr.io/owasp-dep-scan/dep-scan - Node.js + npm(用于
.npmrc和npm config修复),以及访问你的私有仓库(Artifactory、Nexus、Azure Artifacts、GitHub Packages、AWS CodeArtifact)。 - 对被评估的仓库/锁文件具有读取权限,对私有仓库具有写入权限以进行防御性注册。
目标
- 枚举 npm、PyPI、Maven、Composer 和 RubyGems 项目清单中声明的每一个内部依赖。
- 确定哪些内部名称不存在于相应的公共仓库,因而可被认领。
- 区分真实暴露与误报(作用域包、已镜像的名称)。
- 应用仓库固定和作用域限制控制措施,使公共替换不可能发生。
- 为未被认领的内部名称防御性注册占位包。
- 在 CI 中建立定期检测控制措施,以捕获新引入的可混淆依赖。
MITRE ATT&CK 映射
| 技术 ID | 技术名称 | 相关性 |
|---|---|---|
| T1195.001 | 供应链入侵:入侵软件依赖项和开发工具 | 核心技术 — 攻击者用恶意公开包替换内部依赖项。 |
| T1195.002 | 供应链入侵:入侵软件供应链 | 更广泛的类别,涵盖混淆攻击成功后产生的被入侵构建产物。 |
| T1059.007 | 命令和脚本解释器:JavaScript | npm 的 preinstall/postinstall 生命周期脚本在解析时执行攻击者的 JavaScript。 |
| T1071.001 | 应用层协议:Web 协议 | 被替换的包将窃取的环境变量/凭据回传到攻击者的 HTTP(S) 端点。 |
工作流
1. 清点代码库中的所有清单文件
定位每一个依赖清单,确保没有遗漏。
# Find all supported manifests in a monorepo
find . -type f \( \
-name package.json -o \
-name requirements.txt -o \
-name pom.xml -o \
-name composer.json -o \
-name Gemfile.lock \
\) -not -path '*/node_modules/*' -print
2. 使用 confused 扫描 npm 清单
confused 读取清单并报告每一个在公共注册表中未找到的依赖名称 — 这些都是混淆攻击的候选目标。
# npm (default language is npm)
confused -l npm package.json
# Treat your known-good scopes as secure to suppress false positives (supports wildcards)
confused -l npm -s '@acme/*,@acme-internal/*' package.json
# Verbose, to see each lookup
confused -l npm -v package.json
3. 扫描 PyPI、Maven、Composer 和 RubyGems 清单
-l 标志用于选择生态系统;每个生态系统对应其标准清单文件。
confused -l pip requirements.txt # PyPI -> requirements.txt
confused -l mvn pom.xml # Maven -> pom.xml
confused -l composer composer.json # PHP -> composer.json
confused -l rubygems Gemfile.lock # Ruby -> Gemfile.lock
4. 使用 OWASP dep-scan 私有命名空间模式交叉检查
dep-scan 确认已声明的私有命名空间是否存在混淆暴露,并将其纳入更广泛的风险审计。
# Flag private namespaces accidentally claimable on public registries
depscan --src $PWD --reports-dir ./reports \
--private-ns acme,acme_internal,@acme
# Enable deep package risk audit (npm + pypi): typosquats, takeover risk, etc.
depscan --src $PWD --reports-dir ./reports --risk-audit
5. 分类候选目标并确认可认领性
对于每个被标记的名称,验证其在公共注册表上确实不存在(404 表示可认领)。
# npm: a 404 status means the name is unregistered on the public registry
curl -s -o /dev/null -w "%{http_code}\n" https://registry.npmjs.org/@acme%2finternal-utils
# PyPI: 404 from the JSON API means the project name is free
curl -s -o /dev/null -w "%{http_code}\n" https://pypi.org/pypi/acme-billing-sdk/json
6. 通过将作用域固定到注册表来修复 npm
将每个内部作用域绑定到私有注册表,确保同名的公开包永远不会被解析。
# .npmrc (project root, committed)
@acme:registry=https://artifactory.example.com/api/npm/npm-internal/
//artifactory.example.com/api/npm/npm-internal/:_authToken=${NPM_TOKEN}
# Force the default registry to a single proxy that does NOT merge public + private
registry=https://artifactory.example.com/api/npm/npm-virtual/
在安装前验证解析来源:
npm config get @acme:registry
npm install --dry-run # confirm @acme/* resolves from the private host
7. 修复 PyPI 和 Maven
固定 Python 索引解析和 Maven 镜像,使公共源无法遮蔽内部制品。
# pyproject.toml (PEP 621 / pip >= 23): explicit index pinning
[tool.pip]
index-url = "https://artifactory.example.com/api/pypi/pypi-internal/simple/"
# Do NOT use extra-index-url for internal packages — pip merges and picks highest version.
<mirrors>
<mirror>
<id>internal-virtual</id>
<mirrorOf>*</mirrorOf>
<url>https://artifactory.example.com/artifactory/maven-virtual</url>
</mirror>
</mirrors>
8. 防御性地注册占位包
对于无法完全隔离的名称,自己以空且无功能的占位包认领公开名称,使攻击者无法认领。
# npm placeholder claim (scoped, public)
mkdir acme-internal-utils && cd acme-internal-utils
npm init -y
npm pkg set version=0.0.1-placeholder description="Reserved internal name. Do not use."
npm publish --access public
9. 将检测接入 CI
如果出现任何新的可混淆依赖项,则使流水线失败。
# .github/workflows/depconfusion.yml
name: dependency-confusion
on: [push, pull_request]
jobs:
confused:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: '1.22' }
- run: go install github.com/visma-prodsec/confused@latest
- name: Scan npm manifest
run: $(go env GOPATH)/bin/confused -l npm -s '@acme/*' package.json
10. 运行捆绑的辅助工具进行批量分类
使用包含的 agent.py 扫描目录树,并输出结合 confused 与实时注册表探测的结构化报告。
python scripts/agent.py --path . --ecosystem npm \
--secure-namespaces '@acme/*,@acme-internal/*' \
--output report.json
工具和资源
| 工具 | 用途 | 来源 |
|---|---|---|
| confused | 检测已声明依赖项中遗留的免费命名空间 | https://github.com/visma-prodsec/confused |
| ConfusedDotnet | 对 NuGet/.NET 执行相同检查 | https://github.com/visma-prodsec/ConfusedDotnet |
| OWASP dep-scan | 风险审计,包括 --private-ns 混淆检查 | https://github.com/owasp-dep-scan/dep-scan |
| OWASP CI/CD Top 10 | CICD-SEC-03 依赖链滥用 | https://owasp.org/www-project-top-10-ci-cd-security-risks/ |
| Birsan 研究 | 最初的依赖混淆文章 | https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610 |
| npm scopes 文档 | 作用域到注册表固定参考 | https://docs.npmjs.com/cli/v10/using-npm/scope |
验证标准
- 代码库中的所有依赖清单均已枚举。
- 为每个相关生态系统运行了
confused,并提供了安全命名空间。 - 运行了 OWASP dep-scan
--private-ns,并与confused输出进行了核对。 - 每个被标记的名称均已确认可认领(404)或被排除为误报。
- 在
.npmrc/pip配置 / Maven 镜像中将内部作用域固定到私有注册表。 - 审查了
extra-index-url和合并的虚拟源,以识别最高版本拉取风险。 - 为无法隔离的名称注册了占位包。
- CI 任务在每次 push/PR 时强制执行该检查。
- 发现已记录,包含负责人和修复状态。