关于 detecting-typosquatting-packages-in-npm-pypi
用于检查 npm 和 PyPI 软件包名称仿冒风险的安全技能,适合依赖审计和供应链监控。它生成名称变体、查询注册表,并结合相似度及软件包元数据对候选项评分。
主要功能
- 01生成多种名称拼写变体
- 02查询 npm 与 PyPI 注册表
- 03比较名称相似度和元数据
- 04依据多项信号计算风险分
- 05输出可疑软件包报告与阻止列表
适用场景
- 01审计项目依赖中的近似名称软件包
- 02监控组织软件包是否遭仿冒
- 03调查疑似拼写错误安装事件
- 04评估软件包加入依赖树前的风险
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill detecting-typosquatting-packages-in-npm-pypi安装后在 Claude Code 中输入/detecting-typosquatting-packages-in-npm-pypi确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdscripts/agent.py详细介绍
检测 npm 和 PyPI 中的域名抢注包
何时使用
- 审计项目依赖,识别名称与流行库高度相似的包
- 主动扫描包注册表,查找新发布的、可能是贵组织包域名抢注的包
- 调查疑似供应链入侵事件,其中开发者安装了拼写错误的包名
- 构建自动监控,当出现名称接近关键依赖的新包时发出警报
- 在将不熟悉的包加入项目依赖树之前,评估其风险状况
请勿使用作为判定恶意意图的唯一依据;仅凭名称相似并不能证明包是恶意的。请勿在未人工审核被标记包的情况下用于批量自动下架请求。未经授权请勿用于私有注册表。
前置条件
- 安装 Python 3.9+ 及
requests和python-Levenshtein(或rapidfuzz)包 - 能够访问
https://pypi.org/pypi/<package>/json(PyPI JSON API)和https://registry.npmjs.org/<package>(npm 注册表 API) - 一份要监控的流行或关键包列表(例如 PyPI 前 1000 个包、组织的依赖列表)
- 了解常见的域名抢注模式:字符遗漏、换位、插入、替换以及连字符/下划线操作
工作流
步骤 1:构建目标包监控列表
建立要监控域名抢注的合法包集合:
- 提取项目依赖:解析
requirements.txt、Pipfile.lock、package.json或package-lock.json,提取所有直接和传递依赖名称 - 包含流行包:从 PyPI 下载量前 1000 的包(可从
https://hugovk.github.io/top-pypi-packages/获取)或按下载量排名的 npm 顶级包中补充高价值目标 - 添加组织包:包含贵组织发布的、攻击者可能通过域名抢注来拦截内部安装的任何包
- 规范化名称:PyPI 将连字符、下划线和句点视为等价(PEP 503 规范化:
re.sub(r"[-_.]+", "-", name).lower())。npm 包名称区分大小写,但作用域包使用@scope/name格式。比较前先规范化。
步骤 2:生成候选域名抢注名称
为每个目标包生成可能的域名抢注变体:
- 字符遗漏:每次移除一个字符(
requests->rquests、requets、reqests) - 字符换位:交换相邻字符(
requests->erquests、rqeuests、reques ts) - 字符替换:使用 QWERTY 距离映射将字符替换为键盘相邻键(
requests->rrquests、requesta) - 字符插入:在每个位置插入常见字符(
requests->rrequests、reqquests) - 分隔符操作:对于带连字符的名称,尝试移除、加倍或替换分隔符(
my-package->mypackage、my--package、my_package) - 常见前缀/后缀攻击:前置或追加常见字符串(
python-requests、requests-python、requests2、requests-lib)
步骤 3:查询注册表 API 以获取候选包
检查生成的候选名称是否确实存在于注册表中:
- PyPI JSON API:对每个候选包发送
GET https://pypi.org/pypi/<candidate>/json。响应200表示该包存在;404表示不存在。从响应中提取:info.name、info.version、info.author、info.summary、info.home_page、info.project_urls和releases(以版本为键,带有upload_time_iso_8601时间戳)。 - npm 注册表 API:发送
GET https://registry.npmjs.org/<candidate>,并带上Accept: application/json。提取:name、description、dist-tags.latest、time.created、time.modified、maintainers和versions。 - 速率限制:PyPI 没有公开的速率限制,但请遵守合理的请求速率(每秒 1-2 个请求)。npm 注册表在受到速率限制时会返回
429;请实现指数退避。 - 批量优化:对于大型候选列表,使用连接池(
requests.Session)并行化请求,并限制并发数以避免触发滥用防护。
步骤 4:分析包元数据以寻找可疑信号
根据多个启发式信号对每个存在的候选包进行评分:
- Levenshtein 距离:计算候选名称与目标之间的编辑距离。与热门包距离为 1-2 的包是高优先级嫌疑对象。历史分析显示,40 个已知的域名抢注包中有 18 个与其目标的 Levenshtein 距离为 2 或更小。
- 发布日期的新近程度:将候选包的首次发布日期与目标包的进行比较。在其近似同名包出现数年后才创建的包更加可疑。标记在过去 90 天内创建、且与数年前发布的包相似的包。
- 下载量差异:比较每周下载量。合法的相似名称包通常具有相当或可解释的下载量。一个下载量为 50 的包与其下载量为 500 万的近似同名包相比十分可疑。PyPI 下载统计可通过 BigQuery(
pypistats.org/api/)获取;npm 在https://api.npmjs.org/downloads/point/last-week/<package>提供下载量。 - 作者和维护者分析:检查候选包的作者是否与合法包的作者匹配。近似相同的名称却有不同的作者会增加可疑度。
- 描述相似度:比较包描述。域名抢注包经常复制或紧密改写目标包的描述以显得合法。
- 版本数量:合法包通常随时间推移有许多版本。一个只有 1-2 个版本、且名称与热门包相似的包十分可疑。
- 仓库 URL 分析:检查候选包是否链接到与目标相同的仓库(可能是合法的复刻/镜像),或者没有仓库 URL(可疑)。
步骤 5:评分、排序并报告发现
将各信号组合成一个综合风险评分,并生成可操作的报告:
- 加权评分:为每个信号分配权重。示例:Levenshtein 距离 1 = 40 分,Levenshtein 距离 2 = 25 分,创建于 90 天前以内 = 15 分,下载量比率 < 0.001 = 15 分,作者不同 = 10 分,单一版本 = 5 分。总分为 100 分。
- 阈值分类:得分 >= 70:高风险(很可能是域名抢注),40-69:中风险(需要人工审核),< 40:低风险(很可能是合法的)
- 生成报告:对于每个被标记的包,包含它所模仿的目标、所有信号值、综合得分、两个包在注册表上的直接链接,以及一项建议(阻止、调查或允许)
- 可操作的输出:生成一个被标记包名称的阻止列表,可导入到包管理器拒绝列表、CI/CD 策略引擎或制品仓库代理规则中
关键概念
| 术语 | 定义 |
|---|---|
| 域名抢注(Typosquatting) | 注册一个与热门包名高度相似的包名,利用常见拼写错误诱使开发者安装恶意代码 |
| Levenshtein 距离 | 将一个字符串转换为另一个字符串所需的最少单字符编辑次数(插入、删除、替换);衡量名称相似度的主要指标 |
| 依赖混淆(Dependency Confusion) | 一种更广泛的供应链攻击,攻击者将名称与私有内部包匹配的恶意包发布到公共仓库,利用包管理器解析顺序进行攻击 |
| PEP 503 规范化 | Python 打包规范,将包名中的连字符、下划线和句点视为等价,即 my-package、my_package 和 my.package 解析为同一个包 |
| QWERTY 距离 | 一种考虑键盘布局的距离度量,衡量标准键盘上两个按键之间的距离,用于检测相邻按键误输入导致的替换 |
| 组合抢注(Combosquatting) | 域名抢注的一种变体,攻击者在包名前或后附加常见词汇(例如 requests-security、python-requests) |
| StarJacking | 一种攻击方式,抢注包将其仓库 URL 链接到合法包的 GitHub 仓库,以虚增表面可信度 |
工具与系统
- PyPI JSON API:位于
https://pypi.org/pypi/<package>/json的 REST API,返回包元数据,包括名称、作者、版本、上传时间戳和项目 URL - npm Registry API:位于
https://registry.npmjs.org/<package>的 REST API,返回包元数据,包括维护者、版本历史、创建时间戳和分发信息 - python-Levenshtein / rapidfuzz:用于快速字符串距离计算的 Python 库,支持 Levenshtein、Damerau-Levenshtein、Jaro-Winkler 及其他相似度指标
- pypistats.org API:提供 PyPI 包的下载统计数据,可用于比较疑似抢注包与其目标包的下载量
- npm 下载量 API:位于
https://api.npmjs.org/downloads/point/<period>/<package>的端点,提供 npm 包的下载统计数据
常见场景
场景:审计 Python 项目中的仿冒名称依赖
背景:一个安全团队发现某位开发者的工作站因安装了一个 Python 包而遭入侵。事件响应团队需要审计所有项目依赖,排查潜在的仿冒名称包,并建立持续监控。
方法:
- 解析
requirements.txt和Pipfile.lock,提取全部 87 个直接依赖和传递依赖 - 针对每个依赖,使用字符遗漏、换位、替换和分隔符操作生成仿冒名称候选,约产生 2,400 个候选名称
- 对每个候选名称查询 PyPI JSON API,发现其中 34 个确实作为已发布包存在
- 对每个存在的候选进行评分:3 个包得分高于 70(HIGH 风险),其 Levenshtein 距离为 1、创建于最近 60 天内、只有一个版本,且下载量少于 100
- 人工复核确认这 3 个中有 2 个是恶意仿冒名称包,其中包含混淆代码,会在安装期间窃取环境变量
- 在组织的制品代理中封禁这些恶意包,通过
[email protected]向 PyPI 报告以请求下架,并将全部 87 个依赖加入持续监控观察列表 - 将检测 Agent 实现为每周运行的定时 CI 任务,并在发现新的 HIGH 风险结果时发出告警
陷阱:
- 在比较前未按 PEP 503 规范化 PyPI 包名,导致连字符与下划线变体之间漏配
- 将 Levenshtein 距离阈值设得过低(仅为 1),漏掉距离为 2、使用双重替换的仿冒名称包
- 仅依赖名称相似度而不检查元数据信号,导致对合法相似包名产生高误报率
- 未考虑 npm 作用域包(
@scope/name),其命名规则与非作用域包不同 - 过于频繁地查询注册表,导致被限流或 IP 被封禁
输出格式
## Typosquatting Detection Report
**Scan Date**: 2026-03-19
**Registry**: PyPI
**Packages Monitored**: 87
**Candidates Generated**: 2,412
**Candidates Found in Registry**: 34
**Flagged as Suspicious**: 5
### HIGH Risk (Score >= 70)
| Suspect Package | Target Package | Levenshtein | Created | Downloads | Score |
|----------------|---------------|-------------|---------|-----------|-------|
| reqeusts | requests | 1 | 2026-02-28 | 43 | 92 |
| requsets | requests | 1 | 2026-03-01 | 12 | 88 |
| numpyy | numpy | 1 | 2026-01-15 | 67 | 78 |
### Recommendation
- BLOCK: reqeusts, requsets, numpyy (add to artifact proxy deny-list)
- REPORT: Submit malware reports to [email protected] with package names and evidence
- MONITOR: Continue weekly scans for the full dependency watchlist
评论
支持 Markdown