detecting-typosquatting-packages-in-npm-pypi について
npm と PyPI のパッケージ名のタイポスクワッティングリスクを調べるセキュリティスキルです。依存関係の監査やサプライチェーン監視向けに、名前候補を生成してレジストリを照会し、類似度とメタデータで評価します。
主な機能
- 01複数のパッケージ名候補を生成
- 02npm と 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 のタイポスクワッティングパッケージを検出する
使用する場面
- プロジェクトの依存関係を監査し、人気のあるライブラリと名前が不審なほど似ているパッケージを特定する場合
- 組織のパッケージを模倣したタイポスクワッティングの可能性がある新規公開パッケージを、パッケージレジストリで事前にスキャンする場合
- 開発者が誤字のあるパッケージ名をインストールしたサプライチェーン侵害の疑いを調査する場合
- 重要な依存関係に名前が似ている新しいパッケージが公開されたときに警告する、自動監視を構築する場合
- プロジェクトの依存関係ツリーに追加する前に、見慣れないパッケージのリスクを評価する場合
悪意の有無を判断する唯一の根拠として使用しないでください。名前が似ているだけでは、パッケージが悪意のあるものだとは証明できません。フラグが付いたパッケージを人手で確認せずに、一括で削除要請を行う用途には使用しないでください。許可なくプライベートレジストリに対して使用しないでください。
前提条件
requestsとpython-Levenshtein(またはrapidfuzz)パッケージをインストールした Python 3.9+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を解析して、直接依存関係と間接依存関係の名前をすべて抽出します - 人気のあるパッケージを含める:
https://hugovk.github.io/top-pypi-packages/で公開されている PyPI ダウンロード数上位 1000 パッケージや、ダウンロード数上位の npm パッケージから、価値の高い対象を追加します - 組織のパッケージを追加する:攻撃者が内部インストールを傍受するために模倣する可能性のある、組織が公開したパッケージを含めます
- 名前を正規化する:PyPI ではハイフン、アンダースコア、ピリオドを同じものとして扱います(PEP 503 の正規化:
re.sub(r"[-_.]+", "-", name).lower())。npm のパッケージ名では大文字と小文字が区別されますが、スコープ付きパッケージには@scope/name形式が使われます。比較の前に正規化してください。
ステップ 2:タイポスクワッティング候補の名前を生成する
各対象パッケージについて、タイポスクワッティングの候補となる名前のバリエーションを生成します。
- 文字の省略:文字を一度に 1 つずつ削除します(
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:
Accept: application/jsonを指定してGET https://registry.npmjs.org/<candidate>を送信します。次の情報を抽出します:name、description、dist-tags.latest、time.created、time.modified、maintainers、およびversions。 - レート制限:PyPI は公開されたレート制限を設けていませんが、妥当なリクエスト頻度(毎秒 1~2 件)を守ってください。npm レジストリはレート制限に達すると
429を返すため、指数バックオフを実装してください。 - バッチ処理の最適化:候補リストが大きい場合は、接続プーリング(
requests.Session)を使ってリクエストを並列化し、不正利用防止機能が作動しないよう同時実行数を制限してください。
ステップ 4:パッケージのメタデータを分析して不審な兆候を調べる
存在する候補パッケージごとに、複数のヒューリスティックな指標を使ってスコアを付けます。
- レーベンシュタイン距離:候補名と対象名の編集距離を計算します。人気パッケージとの距離が 1~2 のパッケージは、優先度の高い疑わしい候補です。過去の分析では、既知のタイポスクワッティング 40 件のうち 18 件で、対象とのレーベンシュタイン距離が 2 以下でした。
- 公開日の新しさ:候補の初回公開日と対象の初回公開日を比較します。類似した名前のパッケージより何年も後に作成されたパッケージは、より疑わしいといえます。何年も前に公開されたパッケージに似ていて、直近 90 日以内に作成されたパッケージにフラグを付けます。
- ダウンロード数の差:週間ダウンロード数を比較します。正当な類似名のパッケージは、通常、同程度のダウンロード数か、説明可能な差があります。類似名のパッケージが 500 万回ダウンロードされている一方で、あるパッケージのダウンロード数が 50 回しかない場合は疑わしいといえます。PyPI のダウンロード統計は BigQuery(
pypistats.org/api/)から取得できます。npm ではhttps://api.npmjs.org/downloads/point/last-week/<package>でダウンロード数を取得できます。 - 作成者とメンテナーの分析:候補パッケージの作成者が正当なパッケージの作成者と一致するか確認します。名前がほぼ同一なのに作成者が異なる場合、疑わしさが高まります。
- 説明の類似性:パッケージの説明を比較します。タイポスクワッティングでは、正当なパッケージに見せかけるため、対象パッケージの説明をコピーしたり、よく似た言い回しにしたりすることがよくあります。
- バージョン数:正当なパッケージは通常、長期間にわたって多数のバージョンがあります。人気パッケージに似た名前で、バージョンが 1~2 個しかないパッケージは疑わしいといえます。
- リポジトリ URL の分析:候補のリンク先が対象と同じリポジトリか(正当なフォークやミラーの可能性があります)、リポジトリ URL がないか(疑わしい)を確認します。
ステップ 5:スコアを付け、順位付けし、調査結果を報告する
各指標を組み合わせて総合リスクスコアを算出し、実行可能なレポートを作成します。
- 重み付けスコアリング:各指標に重みを割り当てます。例:レーベンシュタイン距離 1 = 40 点、レーベンシュタイン距離 2 = 25 点、作成から 90 日未満 = 15 点、ダウンロード比率 < 0.001 = 15 点、作成者が異なる = 10 点、バージョンが 1 つのみ = 5 点。合計スコアは 100 点満点です。
- しきい値による分類:スコア >= 70:高リスク(タイポスクワッティングの可能性が高い)、40~69:中リスク(手動レビューが必要)、< 40:低リスク(正当なパッケージの可能性が高い)
- レポートの生成:フラグが付いたパッケージごとに、模倣している対象、すべての指標の値、総合スコア、レジストリ上の両パッケージへの直接リンク、および推奨措置(ブロック、調査、または許可)を記載します。
- 実行可能な出力:パッケージマネージャーの拒否リスト、CI/CD ポリシーエンジン、またはアーティファクトリポジトリのプロキシルールに取り込める、フラグが付いたパッケージ名のブロックリストを作成します。
主な概念
| 用語 | 定義 |
|---|---|
| タイポスクワッティング | 人気のパッケージに非常によく似たパッケージ名を登録し、よくある入力ミスを悪用して、開発者に悪意のあるコードをインストールさせること |
| レーベンシュタイン距離 | ある文字列を別の文字列に変換するために必要な、1文字単位の編集(挿入、削除、置換)の最小回数です。名前の類似度を測定する主要な指標です |
| 依存関係の混同 | 攻撃者が、パッケージマネージャーの解決順序を悪用し、非公開の内部パッケージと同じ名前の悪意のあるパッケージを公開レジストリに登録する、より広範なサプライチェーン攻撃です |
| PEP 503 による正規化 | パッケージ名においてハイフン、アンダースコア、ピリオドを同等に扱う Python パッケージング仕様です。つまり、my-package、my_package、my.package は同じパッケージとして解決されます |
| QWERTY 距離 | キーボード配列を考慮した距離指標です。標準的なキーボード上で2つのキーがどれだけ離れているかを測定し、隣接するキーの入力ミスによる置換の検出に使われます |
| コンボスクワッティング | 攻撃者がパッケージ名の前後に一般的な単語を付け加える、タイポスクワッティングの一種です(例:requests-security、python-requests) |
| スター・ジャッキング | タイポスクワットパッケージがリポジトリ 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 ライブラリです。レーベンシュタイン距離、ダメラウ・レーベンシュタイン距離、Jaro-Winkler 距離など、さまざまな類似度指標をサポートします
- pypistats.org API:PyPI パッケージのダウンロード統計情報を提供し、疑わしいタイポスクワットパッケージと標的パッケージのダウンロード数を比較できます
- npm download counts 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点を超える(リスク:高)スコアとなり、レーベンシュタイン距離が1、作成から60日以内、バージョンが1つ、ダウンロード数が100未満でした
- 手動レビューにより、そのうち2個が悪意のあるタイプミスを狙ったパッケージであり、インストール中に環境変数を窃取する難読化コードを含んでいることが確認されました
- 組織のアーティファクトプロキシで悪意のあるパッケージをブロックし、
[email protected]を通じて PyPI に削除を報告し、87個すべての依存関係を継続監視のウォッチリストに追加します - 検出エージェントを、毎週実行して新たに見つかった高リスク項目を通知するスケジュール済み CI ジョブとして実装します
注意点:
- 比較前に PEP 503 に従って PyPI パッケージ名を正規化しないと、ハイフンとアンダースコアの違いがある名前の一致を見逃す可能性があります
- レーベンシュタイン距離のしきい値を低く設定しすぎる(1のみ)と、2文字の置換を用いた距離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 対応