npm 2025年 保守者のアカウントの乗っ取りと自ら広がるワーム
9月8日、2要素認証の再設定を装うメールで、debug と chalk の系統の包みを出す保守者の npm のアカウントが乗っ取られ、18の包みに悪い版が出た。翌週、入れた環境の資格情報を集め、得た npm のトークンでその保守者の別の包みにも自分を入れて出す Shai-Hulud が広がり、500を超える包みが侵された。どちらも、正規の名前と正規の公開の権限で出た版だった。
要点
- 9月8日の件の入口は、npm の正規の窓口に見せた別のドメインからのメール1通だった。リポジトリは触られず、npm のアカウントだけが取られた。
- 9月8日の悪い版は、ブラウザの中で暗号資産の送り先を書き換えるコードだった。サーバーやコマンドの中では働かないが、ブラウザ向けに束ねた成果物には残る。
- Shai-Hulud は、導入時のスクリプトで資格情報を集め、盗んだ npm のトークンでその保守者の他の包みにも悪い版を出し、それを繰り返して広がった。
- 2つの件を同じ者の仕業とする公表資料は無い。共通するのは、保守者の資格情報1つで、正規の名前の版を誰の確認も無く出せた形である。
- 根本の守りは、保守者の側はフィッシングに強い2要素認証と、長く使う公開のトークンを無くすこと。使う側は、版の固定、出たばかりの版を待つこと、導入時のスクリプトを切ること。
何が起きたか
| 日時 | 出来事 |
|---|---|
| 2025年9月8日 | 保守者が、2要素認証の再設定を求める本物そっくりのメールに応じ、npm のアカウントを取られる。差出人は npm の正規のドメインではなく npmjs.help だった[1] |
| 9月8日 | debug 4.4.2、chalk 5.6.1、ansi-styles 6.2.2 など18の包みに悪い版が出る。保守者は、多く使われる包みを選んだ形だと書く[1] |
| 9月8日 13時35分 協定世界時 | 利用者が debug のリポジトリに、リポジトリに無い版が npm に出ていると issue を立てる[1] |
| 9月8日 | chalk の系統の包みのもう1人の保守者が、悪い版の上に新しい版を出し、乗っ取られたアカウントを包みから外す。悪い版が出ていたのは約2時間だった[8] |
| 9月8日 21時59分 中央ヨーロッパ夏時間 | npm が、影響のある版を全部消したと保守者に伝える。23時48分にアカウントが戻る[1] |
| 9月13日 | 保守者が、私設の登録所や写しに残った悪い版を押し出すため、影響のあった包みの新しい版を出す[1][2] |
| 9月14日 | GitHub が Shai-Hulud の知らせを受ける[5] |
| 9月15日 | 保守者が debug などの告知を出し、CVE が割り当てられる[1][3] |
| 9月22日 | GitHub が、500を超える侵された包みを消し、同じ印を持つ包みの登録を止めたと書き、公開の仕組みを変える計画を出す[5] |
| 9月23日 | CISA が注意を出す。9月16日より前に出た版に固定するよう求める[4] |
| 9月29日 | npm が、書き込みのできる細かい権限のトークンの既定の期限を7日、最長を90日にし、新しい TOTP の2要素認証の登録を止め、古い形式のトークンを失効させる予定を出す[6] |
| 12月9日 | 古い形式のトークンを全部失効させる。npm login は2時間で切れる接続になり、その中の公開には2要素認証が要る[7] |
多くの解説は、9月8日の件と Shai-Hulud を一続きの攻撃として語り、週の導入数を数十億回と書く。保守者、GitHub、CISA の発表は、2つを同じ者の仕業とは書いておらず、CISA の注意は9月8日の件に触れていない[4]。導入数の数字も公表資料には無い。Shai-Hulud が最初にどの包みから始まったかも公表されていない。
9月8日の乗っ取り
| 段 | 何があったか |
|---|---|
| メール | 2要素認証の再設定を求めるメールで、保守者は本物そっくりだったと書く。差出人は support の宛先で、ドメインは npmjs.help だった[1]。 |
| アカウント | 取られたのは個人の npm のアカウントで、GitHub とリポジトリは触られていない[1]。アカウントのメールの宛先は変えられ、CLIの接続のトークンもすぐ全部失効していた。保守者は自分では取り戻せず、npm の窓口を待った[1]。 |
| 悪い版 | debug 4.4.2 は、前の版と働きが同じで、悪いコードが足されていた[2]。保守者のアカウントにある包みのうち、多く使われる物だけが選ばれていた[1]。 |
| 中身 | ブラウザの中で暗号資産の取引を攻撃者の宛先へ向け直そうとする。MetaMask のような財布が狙われた。手元、サーバー、コマンドの環境では働かない。ブラウザ向けに束ねた成果物には残り得るため、作り直しが要る[2][3]。 |
保守者は、GitHub もリポジトリも破られておらず、入口はフィッシングのメールだけだったと書く[1]。止める所は、npm のアカウントの認証と、公開の権限の持ち方にある。
Shai-Hulud の広がり方
- CISA によれば、ワームは環境から資格情報を探し、GitHub の個人アクセストークンと AWS、GCP、Azure の API の鍵を狙った。集めた物は攻撃者の端点へ送り、GitHub の API で
Shai-Huludという名前の公開のリポジトリにも上げた[4]。 - 広がり方は、侵された開発者として npm の登録所に入り、他の包みにコードを入れ、侵した版を公開する、の自動の繰り返しだった[4]。GitHub は、侵された保守者のアカウントから入り、人気のある包みに導入時のスクリプトを入れ、npm のトークンに限らず多くの種類の秘密を盗めたと書く[5]。
- GitHub は、2025年12月の記事で、より広く資格情報をさらし、CI を狙う第2の波を Shai-Hulud 2.0 と呼び、最初の波と区別している[9]。
9月8日の件と比べると、入口は同じく保守者の資格情報だが、Shai-Hulud は次の保守者の資格情報を自分で手に入れる。開発の端末と CI に置かれた npm の資格情報が、広がる燃料になった[9]。8月の Nx s1ngularity も、導入時のスクリプトで手元の資格情報を集めて公開のリポジトリに上げる点で同じ形をしている。
どこで止められたか
| 止める所 | この件で | 限り |
|---|---|---|
| フィッシングに強い2要素認証 | 偽のドメインのメールから乗っ取られた[1]。GitHub は TOTP をやめて FIDO の2要素認証へ移す計画を出した[5]。CISA も GitHub と npm にフィッシングに強い多要素認証を求める[4] | 盗まれたトークンには効かない。トークンを持たない形と組み合わせる |
| 長く使う公開のトークンを無くす | Shai-Hulud は、手元と CI の npm のトークンで広がった[4][5] | 12月9日までは、古い形式のトークンが使えた[7] |
| リポジトリに無い版に気付く | debug 4.4.2 はリポジトリに無い版だった。利用者はそこから気付いた[1] | 気付いた後の削除は npm の窓口しかできなかった。保守者は締め出されていた[1] |
| 固定し、待つ | 悪い版は数時間から1日以内に消えた[1][8]。範囲指定で取り込む側は、その間に入れた | 私設の登録所や写しには残り続け、保守者は新しい版を出し直した[2] |
| 導入時のスクリプトを切る | Shai-Hulud は導入時のスクリプトで動いた[5] | 9月8日の悪い版は、読み込まれた時にブラウザで動くので、これでは止まらない |
守り方
- npm と GitHub の2要素認証を、WebAuthn や passkey のような、相手のドメインを確かめる方式にする。npm は新しい TOTP の登録を止め、WebAuthn と passkey を勧める[6]。メールのリンクから入らず、登録所のサイトを自分で開く。
- CI からの公開は Trusted Publishing にし、長く使う公開のトークンを置かない。設定の後は、2要素認証を求めトークンを拒む公開の設定にする[10]。
- 手元から公開するときは、2時間で切れる接続と2要素認証で行う[7]。開発の端末に、公開の権限を持つトークンを置かない。
- 多く使われる包みは、公開の権限を持つ人を2人以上にし、乗っ取られた人を外して版を出し直せるようにしておく。この件では、chalk の系統はもう1人の保守者がすぐ上書きした[8]。
- ロックファイルで版を固定し、CIは
npm ciで入れる。範囲指定のままの導入は、出たばかりの悪い版を自動で取る。 - 出たばかりの版を待つ。pnpm の
minimumReleaseAgeは既定で1440分待つ。この月の悪い版の多くはその間に消えた[1][8]。設定はAIが持ち込む部品の危険にある。 - 導入時のスクリプトは、許した依存のほかは走らせない。設定は A03 ソフトウェアサプライチェーンの不備 の型2にある。
- 開発の端末と CI に平文の鍵を置かない。置き場所と、漏れたときの順は秘密の鍵の扱いにある。
影響のあった版が木に入っていないか確かめる 架空のアプリ
npm ls debug chalk # 木のどこに入っているか
└─┬ example-ui@2.3.0
└── debug@4.4.2 # 影響のあった版
grep -n '"node_modules/debug"' -A 2 package-lock.json
npm cache clean --force # 手元の写しを消す
rm -rf node_modules && npm ci # ロックを直してから入れ直す
- debug の告知は、新しい版へ上げ、
node_modulesを消し、包みの管理の全体の写しを消し、ブラウザ向けの束を一から作り直すよう求める。私設の登録所と写しからは悪い版を消す[2]。 - CISA は、ロックファイルで影響を受けた包みを洗い出し、成果物の置き場の写しも探し、開発者の資格情報を全部回し、
webhook.siteへの外向きの通信を止め、使っていない GitHub App と OAuth のアプリを外すよう求める[4]。
2018年の event-stream では、公開の権限は頼んできた者に正規に渡された。この件では、権限を持つ保守者自身の資格情報が奪われた。入口は違うが、取り込む側の守りは同じで、固定、差分、待つこと、導入時のスクリプトを切ることである。
参照
- Version 4.4.2 published to npm is compromised debug-js/debug の issue と、保守者の時刻付きの更新、2025年9月8日から15日
- GHSA-4x49-vf9v-38px debug-js/debug、GitHub Security Advisory、2025年9月15日
- CVE-2025-59144 NIST NVD
- Widespread Supply Chain Compromise Impacting npm Ecosystem CISA、2025年9月23日
- Our plan for a more secure npm supply chain GitHub、2025年9月22日
- Strengthening npm security: Important changes to authentication and token management GitHub Changelog、2025年9月29日
- npm classic tokens revoked, session-based auth and CLI token management now available GitHub Changelog、2025年12月9日
- Version 5.6.1 published to npm is compromised chalk/chalk の issue と保守者の書き込み、2025年9月8日
- Strengthening supply chain security: Preparing for the next malware campaign GitHub、2025年12月
- Trusted publishing for npm packages npm Docs