axios 2026年 保守者の端末からの悪い版の公開
偽の会社との会議で「更新が要る」と言われて入れた物が遠隔操作の道具だった。攻撃者は主な保守者の端末から npm のアカウントを使い、本体のコードに触れず、依存を1つ足しただけの版を2つ出した。CI からの信頼された公開はすでにあったが、個人のアカウントからの公開の道が残っていた。
要点
- 入口は主な保守者の個人の端末だった。偽の会社の Slack と Teams の会議で信用させ、会議の中で「古くなっている物」を入れさせ、それが遠隔操作の道具だった。
- 保守者は npm で2段階の認証を有効にしていた。端末を握られると、その端末で済む認証は守りにならない。
- 悪い版は axios のコードを変えず、使われない依存 plain-crypto-js を足しただけだった。導入時のスクリプトが走り、macOS、Windows、Linux に遠隔操作の道具を入れ、自分の跡を消した。
- 直前の 1.14.0 は CI から OIDC の信頼された公開で出ていた。悪い版は個人のアカウントから出た。安全な公開の道を足しても、古い道を閉じなければ攻撃者は古い道を使う。
- 見つけたのは利用者の側で、開発元に不正な公開を知る仕組みは無かった。攻撃者は乗っ取ったアカウントで報告の issue を消した。
何が起きたか
| 日時 UTC | 出来事 |
|---|---|
| 2026年2月10日 | Google Cloud の Mandiant が、偽のビデオ会議で「不具合の直し」と称して命令を走らせる UNC1069 の手口を公表する[6]。主な保守者は後に、自分が受けた手口はこれに似ていると書いた[2] |
| 3月31日の約2週前 | 主な保守者への働きかけが始まる。初めに端末が侵害された正確な日時は分かっていない[1] |
| 3月30日 05:57 | npm に plain-crypto-js 4.2.0 が出る[1] |
| 3月31日 00:21 | axios 1.14.1 が出る。依存に plain-crypto-js 4.2.1 が足されていた[1] |
| 3月31日 01:00頃 | 0.30.4 が出る。同じ頃に外で見つかり、利用者が issue を立てるが、攻撃者は乗っ取ったアカウントでそれを消す[1] |
| 3月31日 01:38 | 共同の保守者が、悪い版を非推奨にする PR を出し、消された issue を知らせ、npm に直接連絡する[1] |
| 3月31日 03:15 | axios の悪い版が npm から消える[1] |
| 3月31日 03:29 | plain-crypto-js が npm から消える[1] |
| 4月1日 | Microsoft が対処の手引きを出す[4]。シンガポールの CSA が勧告を出す[5] |
| 4月2日 | 主な保守者が事後報告を出す。会議を使った手口を issue の中で説明する[1][2] |
| 4月20日 | CISA が注意喚起を出す[3] |
- 開発元は、事後報告の時点で、どのように入られたかの確かな詳細は無いと書いた[1]。会議の手口は、主な保守者が同じ issue の書き込みで説明したもので[2]、開発元の調査の結論ではない。
- Microsoft は、この件を北朝鮮の国家の攻撃者 Sapphire Sleet によるものとした[4]。CISA の注意喚起は攻撃者の名前を挙げていない[3]。
通った道筋
| 段 | 何があったか |
|---|---|
| 近づく | 実在する会社の創業者になりすまし、その会社に似せた Slack に招いた。中には投稿を共有するチャンネルや、社員や他の OSS の保守者らしい利用者がいた。続いて Teams で、複数の参加者がいるように見える会議を開いた[2]。 |
| 端末に入る | 会議の中で、端末の何かが古いと告げられ、Teams に関わる物と思って入れた物が遠隔操作の道具だった[2]。開発元の要約では、狙いを定めた働きかけと遠隔操作の道具で主な保守者の PC に入り、npm のアカウントの資格情報を得た[1]。 |
| 公開する | 悪い版は主な保守者のアカウントから出た[1]。保守者は2段階の認証を有効にしていたが、道具が入った後は端末の全部を握られていたと書く[2]。どの資格情報で公開したかは公表されていない。 |
| 報告を消す | 利用者が立てた issue を、攻撃者は乗っ取ったアカウントで消した。気付いた共同の保守者は、乗っ取られたアカウントより弱い権限しか持っていなかった[1]。共同の保守者は、自分たちで配布を止める手段が無かったため40分から50分を失ったと書く[10]。 |
| 導入時に走る | 悪い版は axios のソースを変えず、plain-crypto-js 4.2.1 を依存に足した。この依存は axios のコードから一度も読まれず、導入時のスクリプトを走らせるためだけにある。スクリプトは node setup.js を呼び、OS を見分けて次の段を取りに行く[4]。 |
| 跡を消す | 次の段を起動した後、setup.js と導入の仕掛けを書いた package.json を消し、用意しておいた別のファイルを package.json に名前を変えて置く。後から開いても、きれいな定義に見える[4]。 |
| 居座る | macOS、Windows、Linux のそれぞれに遠隔操作の道具を置く[1][4]。Windows では再起動のたびに道具を取り直す仕掛けを登録する[4]。 |
2つの公開の道
npm の登録簿の記録では、直前の 1.14.0 は GitHub Actions から OIDC の信頼された公開で出て、出どころの証明が付いている。同じ系列の前の 1.13.0 と、0.x の系列の 0.30.3 は主な保守者のアカウントから出ていた[7]。開発元は、個人のアカウントから直接出せることが避けられた危険で、OIDC の流れと書き換えられない公開の仕組みは事故の前にあるべきだったと書いた[1]。
悪い版は登録簿から消されたので、その版に出どころの証明が無かったことは、今の記録では確かめられない。確かなのは、事故の前に CI の道が使われていたことと、開発元が個人のアカウントからの公開を危険として挙げたことである[1][7]。
どこで止められたか
| 止める所 | この件で | 限り |
|---|---|---|
| 会議の中で物を入れない | 会議の中で古いと告げられた物を入れた[2]。同じ型の手口は、悪い版の公開の7週前に公表されていた[6] | 作り込みが深く、見破る前提の守りは続かない。入れられた後も公開に届かない形が別に要る |
| 公開を CI の道だけにする | CI の道はあったが、個人のアカウントの道も開いていた[1][7] | npm の「トークンを認めない」設定は従来のトークンだけに効く[8]。端末を握られた保守者の対話の操作で何ができるかは、別に確かめる |
| 自分の公開を見張る | 開発元に不正な公開を知る仕組みは無く、利用者が気付くのを待った[1] | 見張りは公開の後で鳴る。止めるまでの手段も要る |
| 導入時に走らせない | 悪さは導入時のスクリプトだけで起き、axios のコードは使われなかった[4]。版を固定して新しく入れなかった利用者は影響を受けていない[1] | 実行の時に悪さをする版には効かない |
| 入れた機械を侵害として扱う | 開発元も CISA も、戻すだけでなく鍵の回しを求めた[1][3] | CI の使い捨ての実行でも、その実行に渡した秘密は回す[3] |
守り方
部品を出す側
根本の直し方
- 公開の道を CI の信頼された公開だけにする。npm は、信頼された公開の設定と合わせて、公開の権限で「2段階の認証を求め、トークンを認めない」を選ぶよう案内している[8]。新しい道を足したら、古い道を閉じたかを必ず確かめる。この件では古い道が残っていた[1][7]。
- CI の公開に人の承認を挟む。npm は、デプロイの環境で承認を足すこと、信頼された公開を段階の公開だけに絞り、保守者が中身を見てから出す形にすることを勧める[8]。環境の名前は npm の側でも指定できる。主な保守者は事故の後、これに合わせると書いた[2]。
- 公開の権限を持つ人の数を絞り、毎日使う端末と公開を承認する手段を分ける。CISA は、開発者のアカウント全部にフィッシングに強い多要素認証を求める[3]。
- CI を通らない公開を見張る。自分の包みの新しい版が出たら、出どころの証明の有無と、どの実行から出たかを確かめ、合わなければ鳴らす。開発元が欠けていたと書いた点である[1]。
- 止める権限を、止める人に持たせる。この件では、気付いた共同の保守者の権限が、乗っ取られたアカウントより弱かった[1]。
公開の道を1つにした架空の設定 example-lib
# npm の包みの設定
信頼された公開 GitHub Actions example-org/example-lib release.yml
環境の名前 npm-publish
公開の権限 2段階の認証を求め、トークンを認めない
# .github/workflows/release.yml の一部
jobs:
publish:
environment: npm-publish # 承認者の許可が要る環境
permissions:
contents: read
id-token: write # OIDC はこのジョブだけ
部品を使う側
根本の直し方
- ロックファイルで版を固定し、CI では
npm ciで入れる。範囲指定で入れ直した時に、出たばかりの悪い版が入る。開発元は、固定していて3時間の間に入れ直さなかった利用者は影響が無いと書く[1]。 - 導入時のスクリプトを止める。CISA は
.npmrcにignore-scripts=trueを置くよう勧めた[3]。止めたときの動きは A03 ソフトウェアサプライチェーンの不備 の型2にある。 - 出たばかりの版を入れない。CISA は、公開から7日経った版だけを入れる設定を勧めた[3]。この件の悪い版は約3時間で消えた[1]。
# .npmrc 架空の開発の現場。CISA の勧めの2行
ignore-scripts=true
min-release-age=7
緩和の策
npm audit signaturesは、取得した包みの出どころの証明も確かめる[9]。前の版にあった証明が新しい版で消えたことは、別に見る。- 依存を自動で上げる道具を、すぐ取り込む設定にしない。Microsoft は、Dependabot や Renovate の自動の更新を axios について止めるか絞るよう勧めた[4]。
- 導入の時の子のプロセスと外への通信を見張る。CISA は、導入や更新の時の思わぬ子のプロセスと通信を見るよう求めた[3]。
- 悪い版を入れた機械は、版を戻すだけでなく、その機械から届く鍵を全部回す。リポジトリ、CI、クラウド、npm、SSH の鍵が対象になる[3]。順は 秘密の鍵の扱い の「漏れたときの順」に沿う。
- 導入の後の
node_modulesの中身を見て安心しない。この件の仕掛けは導入の後に自分の跡を消した[4]。ロックファイルとログで、入れた版と時刻を確かめる。
AIが入れる依存
AIのコーディングの道具は、依存を足し、版を上げ、npm install を自分で走らせる。この件の悪い版は、範囲指定で取り込む新しい版として届き、導入した瞬間に動いた。
- AIに作業させる端末と CI でも、導入時のスクリプトを止め、出たばかりの版を入れない設定を先に置く。設定はリポジトリに置き、AIの判断に任せない。置き方は 決まりを文書に残す にある。
- AIが出した依存の追加と版の上げは、人が出どころを確かめてから入れる。止め方は AIが持ち込む部品の危険 にある。
- AIに作業させる端末に、公開の権限を持つ資格情報を置かない。端末を握られた時に届く先が、そのまま被害の範囲になる。
型としての整理は A03 ソフトウェアサプライチェーンの不備 の型2に当たる。
参照
- Post Mortem: axios npm supply chain compromise axios、主な保守者、2026年4月2日。4月18日に閉じられた
- 同じ issue への主な保守者の書き込み axios、2026年4月2日。2段階の認証は 同日の別の書き込み、環境の設定は 4月6日の書き込み
- Supply Chain Compromise Impacts Axios Node Package Manager CISA、2026年4月20日
- Mitigating the Axios npm supply chain compromise Microsoft Threat Intelligence、2026年4月1日
- Advisory on Axios Supply Chain Attack via Compromised npm Account Cyber Security Agency of Singapore、2026年4月1日
- UNC1069 Targets Cryptocurrency Sector with AI-Enabled Social Engineering Mandiant、Google Cloud、2026年2月10日
- axios の登録簿の記録 npm registry。2026年11月10日に確認した各版の公開者と出どころの証明
- Trusted publishing for npm packages npm Docs
- npm-audit npm Docs
- 同じ issue への共同の保守者の書き込み axios、2026年4月3日