はじめに
「怪しいnpmパッケージが出た。うちのプロジェクトは大丈夫だろうか」
こういうとき、以前なら人間がpackage-lock.jsonを検索し、npm lsを実行し、Gitの履歴を確認していました。
今なら、CodexやClaude CodeなどのAIエージェントに、
このリポジトリ、自分で調べて
と頼みたくなります。
ところが今回のChainDropでは、そのAIエージェント自身が再感染の起点になり得る点まで考える必要があります。
Microsoft Threat Intelligenceは2026年8月4日、keyv、flat-cache、cache-managerなど400を超えるnpmパッケージに、自己伝播型の認証情報窃取マルウェア「Mini Shai-Hulud」の亜種が混入したサプライチェーン攻撃を報告しました。悪性版ではnpmのpreinstallを使って、インストール完了前に悪性コードを実行します。1
さらに厄介なのは、攻撃者がGitHubリポジトリへ、
.claude/settings.json.claude/setup.mjs.vscode/tasks.json.vscode/setup.mjs
などを挿入し、その後のClaudeやVisual Studio Codeの利用を再実行経路として利用できる点です。1
つまり今回確認すべきなのは、
「悪性パッケージが今入っているか」
だけではありません。
過去に入って実行された可能性があるか、認証情報が抜かれていないか、AIエージェントやIDEに永続化されていないかまで確認する必要があります。
本稿では、公式資料と一次調査を中心に、次の流れで整理します。
- ChainDropで何が起きたのか
- AIエージェントに安全に自己点検させる方法
- 悪性版が見つかった場合の復旧方法
- npm 12などを使った再発防止
- 何をもって「復旧完了」とするか
1. ChainDropは「脆弱性」ではなく、正規の配布経路を使った攻撃
まず重要なのは、今回の問題を通常のCVE対応と同じように考えないことです。
正規パッケージの「悪性版」が公開された
Microsoftの調査によれば、攻撃者は取得したnpm公開権限を使い、既存パッケージのtarballへ悪性コードを追加し、パッチバージョンを1つ上げて再公開していました。1
典型的には次のような流れです。
正規メンテ/CI/CD
↓
認証情報・公開権限を窃取
↓
正規npmパッケージに悪性コードを挿入
↓
patch versionを上げてnpmへ公開
↓
利用者がnpm install / npm ci
↓
preinstallでsetup.mjsを実行
↓
認証情報を探索・窃取
↓
取得した公開権限で別パッケージへ伝播
preinstallはnpmが正式に提供しているライフサイクルスクリプトです。
つまり、アプリケーションを起動していなくても、依存関係をインストールした時点でコードが動き得ます。2
Microsoftは、悪性版をライフサイクルスクリプト有効状態でインストールした開発端末やCI/CDランナーについて、潜在的に侵害されたものとして扱うよう推奨しています。1
npm auditだけでは判定できない
ここはかなり重要です。
npm auditは、依存関係についてnpm Registryへ報告されている既知のセキュリティ脆弱性を検査する仕組みです。3
一方、今回確認したいのは、
package名 + 悪性version
への一致です。
したがって、
npm audit
が0件だったとしても、
ChainDropの悪性版を過去にインストールしていない
という証明にはなりません。
また、
npm audit signatures
ではレジストリ署名やprovenance、つまり「どの公開経路から生成されたか」を検証できます。4
しかし今回Microsoftは、GitHub Actionsの正規OIDC公開フローを悪用した場合、悪性パッケージであってもvalid provenanceを持ち得ると確認しています。1
したがって、
| 確認方法 | 分かること | ChainDrop判定 |
|---|---|---|
npm audit |
既知の脆弱性 | 補助 |
npm audit signatures |
署名・provenanceの整合性 | 補助 |
| package名+versionのIOC照合 | 悪性版との一致 | 重要 |
node_modules確認 |
実際に展開された依存関係 | 重要 |
| CI/EDRログ | 悪性コード実行の痕跡 | 重要 |
.claude / .vscode確認 |
永続化・再実行経路 | 重要 |
| 認証ログ・publish履歴 | 侵害後の横展開 | 重要 |
provenanceは「出自」を証明するものであって、「善性」を保証するものではない。
今回の事件は、そこをかなり明確に示しています。
2. AIエージェントに「自分自身」を安全に点検させる
ここからが本稿の実践部分です。
AIエージェントに調査させる場合、いきなり、
このプロジェクトが安全か調べて
とだけ渡すのは避けた方がよいでしょう。
理由は単純です。
AIエージェントが親切に、
npm install
npm ci
npm update
などを実行すれば、確認しようとして悪性コードを実行する可能性があるからです。
さらに今回のマルウェアは.claudeや.vscodeの設定そのものを再実行経路として利用します。疑わしいリポジトリをAIエージェントやIDEで普通に開くこと自体も、使用製品と設定によっては調査前に避けるべきです。1
最初は「読み取り専用」にする
まず、疑わしいリポジトリとは別のクリーンな環境から調査を開始します。
その上で、AIエージェントには次のような指示を渡します。
あなたはソフトウェアサプライチェーン・インシデントの一次調査担当です。
目的:
このリポジトリについて、ChainDrop / Mini Shai-Huludの悪性npm版を
使用した可能性がないか、「読み取り専用」で調査してください。
【禁止事項】
- npm install / npm ci / npm update / npm rebuild を実行しない
- npx / npm exec を実行しない
- pnpm / yarn / bunによるinstallやupdateを実行しない
- package.jsonのscriptsを実行しない
- preinstall / install / postinstall / prepareを実行しない
- .claude / .vscode内のhook、task、scriptを実行しない
- リポジトリ内のJavaScriptを実行しない
- ファイルを削除・変更しない
- 認証情報やtokenの値を画面へ出力しない
- 調査中に依存関係を自動修復しない
【調査内容】
1. package.json、package-lock.json、npm-shrinkwrap.json等を特定する
2. 提供されたIOC一覧について「package名+完全一致version」で照合する
3. 直接依存だけでなく推移的依存も確認する
4. node_modulesが存在する場合、実際に展開されているversionも読み取り専用で確認する
5. 次のファイル・文字列・SHA-256 IOCを検索する
setup.mjs
Math_Symbol.js
Math_init.js
math_<GUID>.js
.claude/settings.json
.claude/setup.mjs
.vscode/tasks.json
.vscode/setup.mjs
npm-cache.com
pypi-get.com
js-mirror.com
Shai-Hulud: Here We Go Again
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc
6. Git履歴からpackage.json、lockfile、.claude、.vscodeへの
不審な変更がないか確認する
7. npmキャッシュについて、IOC versionが存在するか読み取り専用で確認する
8. credentialの内容は読まず、「種類・保存場所・存在有無」のみ整理する
【判定】
各検出事項を次の4段階で分類してください。
- NOT_FOUND:一致なし
- LOCKFILE_MATCH:lockfile上で悪性versionと一致
- INSTALLED_MATCH:node_modulesまたはcache上で一致
- COMPROMISE_INDICATOR:悪性ファイル、hash、hook、実行痕跡などを確認
悪性versionまたはCOMPROMISE_INDICATORを1件でも確認した場合、
この端末上で復旧操作を開始せず、そこで調査を停止してください。
最後に、
「証拠」「パス」「package/versionまたはhash」「判定」「推奨アクション」
の表を作成してください。
Microsoftが公表しているIOCには、setup.mjsやMath_*.jsのSHA-256、npm-cache[.]comなどのC2ドメインが含まれています。1
npm標準コマンドでも静的確認できる
npm 12のnpm lsはインストール済みパッケージと依存関係を表示できます。
npm ls keyv --all
--allを付ければ推移的依存まで展開できます。5
さらに、
npm ls keyv --all --package-lock-only
とすれば、node_modulesではなくpackage-lock.jsonに記録された依存ツリーを確認できます。5
特定versionを調べる場合は、npm 12のnpm queryも利用できます。6
例えば、
npm query '[name="keyv"][version="6.0.0"]' --package-lock-only
です。
同様に、
npm cache ls keyv@6.0.0
などでキャッシュ内の存在も確認できます。
ただし、ここでも注意があります。
現在のlockfileから消えていることと、過去にインストールしていないことは同義ではありません。
例えば、
8月4日 npm ci → 悪性版取得・preinstall実行
8月5日 lockfile更新
8月6日 悪性version削除
という経緯なら、現在のpackage-lock.jsonだけ調べても8月4日の実行は見つかりません。
そのためCIログ、端末ログ、キャッシュ、Git履歴、生成済みartifactまで遡る必要があります。
3. 悪性版が見つかったら「npm uninstall」だけでは戻らない
ここも通常の脆弱性対応とは大きく異なります。
Microsoftは、影響versionをライフサイクルスクリプト有効状態で取り込んだ場合、
- npm
- GitHub
- AWS
- Kubernetes
- HashiCorp Vault
- CI/CD環境
などの認証情報へアクセスされた可能性を調査するよう求めています。1
つまり、
npm uninstall keyv
だけでは復旧したことになりません。
復旧は「クリーン環境」を基準にする
基本的な順序は次のようになります。
疑わしい端末・runnerの利用停止
↓
証跡・ログの保全
↓
別のクリーン端末からcredential失効
↓
npm / GitHub / cloud / CI secret等をrotation
↓
GitHub・npm公開履歴・CI履歴を確認
↓
既知の正常commit/lockfileを特定
↓
開発端末・runner・golden imageを再構築
↓
npm/yarn cache・artifact cacheを破棄
↓
正常ソースから再build
↓
再検証
Microsoftも、資格情報の失効・rotationをknown-clean environmentから実施すること、影響システムおよび下流artifactを信頼できるソースから再構築することを推奨しています。1
1. まず資格情報を止める
npm tokenについては公式ドキュメントから確認・失効できます。7
npm token list
npm token delete <token-id>
ただし、これを疑わしい開発端末から実行するのではなく、クリーン端末から実施します。
GitHubについても、GitHub公式は認証情報侵害が疑われる場合、authorization、key、tokenの失効を推奨しています。8
対象はnpm tokenだけとは限りません。
影響端末やCIからアクセスできた、
- GitHub PAT
- GitHub CLI credential
- SSH key
- npm publish token
- クラウドcredential
- Kubernetes credential
- Vault token
- CI/CD secret
などを、そのcredentialが見えた可能性のある範囲で棚卸しします。
「実際に盗まれたことを確認してから交換する」では遅い場合があります。
2. AIエージェントとIDEの設定も戻す
今回の攻撃では.claudeと.vscodeが永続化経路として利用されます。
したがって、
.claude/
.vscode/
について、現在のファイルだけではなくGit履歴も確認します。
git log --all -- .claude .vscode
git diff <known-good-commit>..HEAD -- .claude .vscode
不審な変更がある場合、単に現在のbranchだけ直すのではなく、
- 他branch
- Pull Request
- workflow
- release branch
- template repository
への展開も確認します。
3. cacheを捨てる
npmのcacheは通常、content-addressed cacheとして整合性確認される仕組みを持っているため、日常運用で頻繁に削除するものではありません。
しかし今回は例外です。
Microsoftは、影響端末やbuild host、特に共有CI cacheについてnpm/yarn cacheをpurgeするよう明示的に推奨しています。1
これは、
hashが正しい=安全
ではないからです。
正規npm Registryから取得した正しく署名された悪性tarballなら、データとしての整合性は正常です。
4. 「掃除」より再構築を優先する
今回のようなcredential stealer+永続化型では、
悪性ファイルを見つけて消したから終了
とは考えない方が安全です。
Microsoftはaffected projectだけでなく、共有base imageやgolden build runnerも再構築するよう推奨しています。1
例えば、新しく払い出した環境で、
git clone <trusted-repository>
cd <repository>
git checkout <known-good-commit>
npm ci --ignore-scripts
npm audit
npm audit signatures
のように、信頼できるcommitとlockfileから再構築します。
npm ciは既存のlockfileを基準にinstallし、package.jsonとlockfileが一致しなければエラーとします。また既存のnode_modulesがあれば削除してからinstallします。9
最初は--ignore-scriptsでライフサイクルスクリプトを止めて確認し、必要なinstall scriptだけを後から許可する方が安全です。
なお、ネイティブモジュールなどinstall scriptを必要とする依存関係もあるため、--ignore-scriptsを恒久的に使えるとは限りません。
そこでnpm 12の仕組みが効いてきます。
4. npm 12で「パッケージを入れたら勝手にコードが動く」を変える
今回Microsoftが対策として明示しているものの一つが、npm CLI v12への更新です。1
npm 12ではdependencyのinstall scriptがデフォルトでブロックされ、allowScriptsで許可されたdependencyだけがpreinstall、install、postinstallなどを実行できます。10
install scriptを「許可制」にする
まず未承認scriptを確認します。
npm approve-scripts --allow-scripts-pending
これはread-onlyで、まだ許可されていないinstall scriptを持つパッケージを一覧表示します。10
必要性をレビューした上で、
npm approve-scripts canvas sharp
のように個別承認します。
npm 12ではデフォルトで、
package@version
単位にpinされます。
したがって、
package Aの1.2.3は許可した
↓
翌日1.2.4が出た
↓
自動的にはinstall scriptを実行しない
という制御が可能です。10
サプライチェーン攻撃に対しては非常に意味のある変更です。
逆に、
npm approve-scripts --all
を機械的に実行してしまえば防御効果が大きく落ちます。
AIエージェントにも--allを勝手に実行させないという運用ルールが必要になります。
新しいversionを即日取得しない
Microsoftはもう一つ、npmのmin-release-age利用を推奨しています。1
例えば.npmrcへ、
min-release-age=7
と設定すると、原則として公開から7日未満のversionをdependency resolutionの候補から外せます。npm公式ドキュメントにも同様の設定例があります。11
これは、
悪性版公開
↓
数時間~数日
↓
研究者・Registry・EDRが検知
↓
悪性版削除・IOC公開
↓
自社ではまだ取得していない
という「時間の防波堤」を作る考え方です。
ただし7日はあくまで設定例です。
重大な脆弱性修正まで7日待たせる可能性があるため、
min-release-age-exclude[]=@myorg/*
などの例外設定や、緊急パッチ用の承認プロセスと組み合わせる必要があります。npm公式も、min-release-ageによってnpm audit fixの修正版がブロックされる場合があることを明記しています。11
これは典型的なトレードオフです。
最新版への追従速度
↑
│
│ trade-off
│
↓
新規悪性versionへの耐性
AIエージェントにも「dependency変更ポリシー」を持たせる
ここからは本稿の提案です。
AIエージェントを利用するリポジトリでは、AGENTS.mdなどのエージェント向けルールに、最低限次のような制約を持たせるとよいでしょう。
## Dependency security policy
- セキュリティ調査中にdependencyをinstall/updateしない
- package-lock.jsonを先に静的解析する
- 新規dependency追加時はpackage名、version、公開日時を報告する
- install scriptを持つdependencyは自動承認しない
- npm approve-scripts --all を実行しない
- repository local hookを無条件で実行しない
- credentialの値をチャットへ出力しない
- provenance正常を「安全」と判定しない
AIエージェントが強力になるほど、
AIに何ができるか
だけでなく、
AIに何を勝手にさせないか
をコードと同じように管理する必要があります。
Trusted Publishingも「それだけ」では足りない
長寿命のnpm tokenを置かずOIDCで公開できるTrusted Publishingは、依然として有効な対策です。
ただし今回のChainDropでは、その正規OIDC workflow自体が悪用される経路も確認されています。1
そのため、
Trusted Publishing
+
branch protection
+
workflow review
+
protected environment
+
人間によるrelease承認
といった多層防御が必要です。
npmには、packageを即時公開せず、maintainerによる確認・承認を挟むStaged Publishingも用意されています。12
ここでも「自動化するか、人間を残すか」ではなく、
危険な境界だけHuman-in-the-Loopにする
という設計が重要になります。
5. 「復旧済み」は何をもって判断するのか
最後に再検証します。
悪性versionを消しただけでは不十分です。
今回のような攻撃では、最低でも次の状態まで確認して初めて「復旧済み」に近づきます。
| 項目 | 完了条件 |
|---|---|
| dependency | 悪性package+versionがlockfile、node_modulesに存在しない |
| cache | 悪性tarball/hashが開発端末・CI・artifact cacheに残っていない |
| endpoint |
setup.mjs、Math_*.jsなどIOCが確認されない |
| AI/IDE |
.claude、.vscodeに不正hook・taskが存在しない |
| credential | 影響範囲のtoken、key、secretをclean hostから失効・rotation済み |
| GitHub | security log、commit、workflow、branch、release履歴を確認済み |
| npm | 不正publishの有無を確認済み |
| CI/CD | runner、base image、golden imageを必要に応じて再構築済み |
| artifact | 影響期間中に作られた成果物を信頼できる環境で再build済み |
| 再発防止 | script allowlist、version pin、release-age policy等を導入済み |
Microsoftも、復旧後にknown-good dependency baselineからbuildし直し、cacheやartifact storeから悪性hashがなくなっていることを確認するよう求めています。1
そして、一番注意したいのが次です。
現在IOCが見つからない
≠
過去に実行されていない
過去に悪性versionをライフサイクルスクリプト有効状態でinstallしていたことが判明した場合は、ファイルが既に消えていても、その当時の開発端末・runnerとcredentialを侵害候補として扱うべきです。1
おわりに
ChainDropを単純化すると、
悪性dependency
↓
install時に実行
↓
credential窃取
↓
正規公開権限を奪取
↓
別packageを悪性化
↓
さらに利用者へ伝播
という自己増殖型のソフトウェアサプライチェーン攻撃です。
しかし今回、もう一つ見逃せない経路が加わりました。
npm package
↓
repository
↓
.claude / .vscode
↓
AIエージェント・IDE
↓
再実行
です。
AIエージェントは、この種のインシデント対応でも非常に有用です。
数百件のIOCとlockfileを照合し、推移的依存を追い、Git履歴を比較し、数十のリポジトリを同じ観点で調べる仕事は、まさにエージェント向きです。
一方で、エージェントへ自由なshell実行権限を与えて、
安全か調べて
だけで任せれば、調査のためにnpm installしてしまうかもしれません。
そこで必要なのは、
AIに調査させる前に、調査方法そのものを制御する
という発想です。
まず読み取り専用で確認する。
悪性versionが見つかったら、その端末で直そうとしない。
clean environmentからcredentialを止め、環境・cache・artifactを再構築する。
そしてnpm 12のinstall script許可制やmin-release-ageによって、次の新規悪性versionを「公開直後に無条件実行しない」環境へ変える。
AIエージェント時代のサプライチェーン対策では、AIそのものを禁止するのではなく、AIにもLeast PrivilegeとHuman-in-the-Loopを適用することが、次の一手になりそうです。
参考
-
Microsoft Security Research - ChainDrop supply chain compromise: Anatomy of a self-propagating worm:攻撃経路、400超の影響パッケージ、preinstall、credential窃取、Claude/VS Codeへの永続化、IOC、復旧・緩和策の根拠 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16
-
npm Docs - Scripts:
preinstallなどnpm lifecycle scriptの仕様 ↩ -
npm Docs - About audit reports:
npm auditがdependencyの既知脆弱性を扱う仕組みの根拠 ↩ -
npm Docs - Viewing package provenance:
npm audit signaturesによるregistry signature/provenance検証 ↩ -
npm Docs - npm ls:
--allによる推移的依存、--package-lock-onlyによるlockfileベースの確認 ↩ ↩2 -
npm Docs - npm query:dependency selectorによるpackage/version検索 ↩
-
npm Docs - Revoking access tokens:npm access tokenの失効手順 ↩
-
GitHub Docs - Revoking your credentials:GitHub credential侵害時の失効対応 ↩
-
npm Docs - npm ci:lockfileを基準としたclean installの仕様 ↩
-
npm Docs - npm approve-scripts:npm 12のdependency install script許可制とversion pinning ↩ ↩2 ↩3
-
npm Docs - Config / min-release-age:公開後一定期間のversionをdependency resolutionから除外する機能 ↩ ↩2
-
npm Docs - Staged publishing:公開前にmaintainer承認を挟むnpmのrelease protection ↩
