npm 2025年 保守者のアカウントの乗っ取りと自ら広がるワーム

9月8日、2要素認証の再設定を装うメールで、debug と chalk の系統の包みを出す保守者の npm のアカウントが乗っ取られ、18の包みに悪い版が出た。翌週、入れた環境の資格情報を集め、得た npm のトークンでその保守者の別の包みにも自分を入れて出す Shai-Hulud が広がり、500を超える包みが侵された。どちらも、正規の名前と正規の公開の権限で出た版だった。

debug 4.4.2 は CVE-2025-59144、CWE-506 埋め込まれた悪意あるコード[3]。9月8日の悪い版は同じ日のうちに npm が消し、保守者は9月13日に、念のため新しい版を出し直した[2]。Shai-Hulud は、GitHub が9月14日に知らせを受け、500を超える包みを消した[5]。CISA は9月23日に注意を出した[4]。npm は12月9日に古い形式のトークンを全部失効させた[7]。保守者の発言、GitHub の告知と記事、CISA の注意で書いています。2026年11月時点。

要点

  • 9月8日の件の入口は、npm の正規の窓口に見せた別のドメインからのメール1通だった。リポジトリは触られず、npm のアカウントだけが取られた。
  • 9月8日の悪い版は、ブラウザの中で暗号資産の送り先を書き換えるコードだった。サーバーやコマンドの中では働かないが、ブラウザ向けに束ねた成果物には残る。
  • Shai-Hulud は、導入時のスクリプトで資格情報を集め、盗んだ npm のトークンでその保守者の他の包みにも悪い版を出し、それを繰り返して広がった。
  • 2つの件を同じ者の仕業とする公表資料は無い。共通するのは、保守者の資格情報1つで、正規の名前の版を誰の確認も無く出せた形である。
  • 根本の守りは、保守者の側はフィッシングに強い2要素認証と、長く使う公開のトークンを無くすこと。使う側は、版の固定、出たばかりの版を待つこと、導入時のスクリプトを切ること。

何が起きたか

9月8日の乗っ取り、9月14日からの Shai-Hulud、9月22日以降の npm の仕組みの変更、12月9日の古いトークンの失効 1 乗っ取り 9月8日 偽のメール1通で 18の包みに悪い版 2 ワーム 9月14日に通報 資格情報で自分を 他の包みへ 3 除去と注意 9月22日、23日 500を超える包み GitHub と CISA 4 仕組みの変更 9月29日の予告 12月9日に古い トークンを失効 1は同じ日のうちに消えた。2は自分で広がり、500を超える包みに及んだ 1の中身は財布を狙うコード、2は資格情報を集めるワーム
図1 9月の2つの件と、その後の npm の変更
日時出来事
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要素認証の再設定のメールに応じ、npm のアカウントを取られ、多く使われる18の包みに悪い版が出て、範囲指定で取り込む利用者とブラウザ向けの成果物に入った 偽のメール 2要素認証の再設定 npm に似た別ドメイン npm のアカウント メールを変えられ トークンを失効され 保守者は締め出し 18の包みに悪い版 リポジトリには無い パッチの版で出す 範囲指定の利用者 ^4.4.0 なら 4.4.2 を取る ブラウザ向けの束に入る ブラウザの中で動く 暗号資産の取引の 送り先を攻撃者へ向ける
図2 偽のメールから利用者のブラウザまで
段何があったか
メール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 の広がり方

侵された版を入れると導入時のスクリプトが資格情報を集め、外と公開のリポジトリへ送り、得た npm の資格情報でその保守者の別の包みに自分を入れて公開し、それを入れた環境で繰り返す 侵された版 正規の名前で公開 導入時のスクリプト 開発の端末かCI 入れた時に走る 環境を走査する 集める GitHub のトークン AWS、GCP、Azure の鍵 npm の資格情報 外へ出す 攻撃者の端点 公開のリポジトリ Shai-Hulud 広がる その保守者として npm に入り、別の 包みに入れて公開 繰り返す 保守者の資格情報が1つ取られるたび、その人の包みが全部次の入口になる
図3 自分で広がる道筋。CISA の注意と GitHub の記事による
  • 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 も、導入時のスクリプトで手元の資格情報を集めて公開のリポジトリに上げる点で同じ形をしている。

どこで止められたか

保守者の認証、公開の権限、登録所、取り込む側、手元の鍵の5つの段に止める所を置く 保守者の認証 公開の権限 登録所 取り込む側 手元の鍵 1 フィッシングに強い2要素認証 鍵がドメインを確かめ、偽のサイトに答えない 2 長く使う公開のトークンを無くす CIからは Trusted Publishing、手元は短い接続 3 リポジトリに無い版に気付く 出どころの証明の無い版を疑う 4 固定し、待ち、スクリプトを切る ロックファイル、待つ日数、ignore-scripts 5 平文の鍵を置かない 集められる物を無くし、取られたら回す
図4 段ごとの止める所
止める所この件で限り
フィッシングに強い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]。
緩和の策
  • GitHub で、ブランチの保護、秘密の走査の通知、Dependabot の更新を有効にする[4]。
  • トークンには必ず期限を付け、決まった間隔で回す。開発はサンドボックスや使い捨ての環境で行う[9]。
  • 公開した成果物を、ソースから作った物と比べる[9]。リポジトリに無い版は、それだけで止める理由になる。

2018年の event-stream では、公開の権限は頼んできた者に正規に渡された。この件では、権限を持つ保守者自身の資格情報が奪われた。入口は違うが、取り込む側の守りは同じで、固定、差分、待つこと、導入時のスクリプトを切ることである。

参照

  1. Version 4.4.2 published to npm is compromised debug-js/debug の issue と、保守者の時刻付きの更新、2025年9月8日から15日
  2. GHSA-4x49-vf9v-38px debug-js/debug、GitHub Security Advisory、2025年9月15日
  3. CVE-2025-59144 NIST NVD
  4. Widespread Supply Chain Compromise Impacting npm Ecosystem CISA、2025年9月23日
  5. Our plan for a more secure npm supply chain GitHub、2025年9月22日
  6. Strengthening npm security: Important changes to authentication and token management GitHub Changelog、2025年9月29日
  7. npm classic tokens revoked, session-based auth and CLI token management now available GitHub Changelog、2025年12月9日
  8. Version 5.6.1 published to npm is compromised chalk/chalk の issue と保守者の書き込み、2025年9月8日
  9. Strengthening supply chain security: Preparing for the next malware campaign GitHub、2025年12月
  10. Trusted publishing for npm packages npm Docs