
【サプライチェーン攻撃】2026年3月31日、npmパッケージ「axios」乗っ取り事件の全容と対策
2026年3月31日、JavaScript エコシステムで最も使われている HTTP クライアント axios が乗っ取られました。
axios の週間ダウンロード数は 1億回超。私自身、ほぼすべての Node.js プロジェクトで使っているくらいに「インフラ」と化しているライブラリです。
この記事では、事件の全容と技術的な仕組み、そして「自分のプロジェクトは大丈夫か」を確認する手順、今後の防御策までを整理します。
事件の概要
| 項目 | 内容 |
|---|---|
| 発生日時 | 2026年3月31日 00:21 UTC(日本時間 9:21) |
| 影響パッケージ | axios@1.14.1、axios@0.30.4 |
| 安全なバージョン | axios@1.14.0 以前 / axios@0.30.3 以前 |
| 公開期間 | 約2〜3時間 |
| 攻撃者 | 北朝鮮系脅威アクター「UNC1069(Sapphire Sleet)」と推定 |
| 同梱マルウェア | plain-crypto-js@4.2.1(ファントム依存関係) |
| ペイロード | クロスプラットフォーム RAT(macOS / Windows / Linux) |
| C2サーバー | sfrclak[.]com:8000 |
axios のメンテナー jasonsaayman 氏の npm アカウントが侵害され、メールアドレスが攻撃者管理の ifstap@proton.me に変更された後、わずか 39分間 で2つの悪意あるバージョンが公開されました。
攻撃のタイムライン
攻撃は約18時間にわたって事前準備されていました。npm の自動スキャンを通過させるための布石が打たれていたのです。
| 時刻 (UTC) | 出来事 |
|---|---|
| 3/30 05:57 | plain-crypto-js@4.2.0 公開(無害なダミー版) |
| 3/30 16:03 | C2サーバー sfrclak[.]com のドメイン登録 |
| 3/30 23:59 | plain-crypto-js@4.2.1 公開(マルウェア入り) |
| 3/31 00:21 | axios@1.14.1 公開、latest タグ付与 |
| 3/31 01:00 | axios@0.30.4 公開、legacy タグ付与 |
| 3/31 00:05頃 | Socket社の自動検知が plain-crypto-js をフラグ |
| 3/31 約03:20 | 悪意あるバージョンが npm から削除 |
「無害な v4.2.0 を先に公開して履歴を作る → 18時間後に v4.2.1 でマルウェア投下」という、典型的な履歴ロンダリングの手口です。新規公開パッケージへの警告フィルタを意図的に回避しています。
攻撃の仕組み
起点:postinstall フック
axios@1.14.1 の package.json には、新しい依存関係が1つだけ追加されていました。
{
"dependencies": {
"plain-crypto-js": "^4.2.1"
}
}そして plain-crypto-js@4.2.1 の package.json には、postinstall フックが仕込まれていました。
{
"scripts": {
"postinstall": "node setup.js"
}
}つまり、npm install を実行するだけで、ユーザー操作なしに setup.js が自動実行されてしまいます。
ファントム依存関係という手口
特筆すべきは、plain-crypto-js が axios のソースコードからは 一度も import / require() されていない ことです。Trend Micro の調査によれば、axios の全86ファイルを grep しても使用箇所は見つかりませんでした。
このように、マニフェストに存在するだけで、コードからは使われない依存関係を「ファントム依存関係(Phantom Dependency)」と呼びます。目的はただ1つ、インストール時の postinstall を発火させることです。
二重の難読化
setup.js は静的解析を回避するため、巧妙に難読化されていました。
- 重要な文字列はすべて
stq[]配列に Base64 でエンコードして格納 - 文字列を反転 → Base64 デコード → XOR 暗号で復号(鍵:
OrDeR_7077) require('child_process')のようなリテラルすら、実行時に動的に解決
これにより、grep "child_process" のような単純な静的スキャナでは検知できません。
OS別のRAT配信
復号後、スクリプトは OS を判定して C2 サーバー(sfrclak[.]com:8000)に接続し、プラットフォーム別の RAT(Remote Access Trojan)をダウンロードします。
| OS | 配信方法 | 保存先 | 偽装名 |
|---|---|---|---|
| macOS | AppleScript + curl | /Library/Caches/com.apple.act.mond | Apple Activity Monitor Daemon |
| Windows | VBScript + PowerShell | %PROGRAMDATA%\wt.exe | Windows Terminal |
| Linux | curl + python3 | /tmp/ld.py | (なし) |
特に macOS のバイナリは、Google 脅威分析グループ(GTIG)の解析で WAVESHAPER バックドアと一致することが確認されています。これは北朝鮮系アクター UNC1069 に帰属するマルウェアです。
証拠隠滅まで自動化
最も恐ろしいのは、ペイロード実行後の アンチフォレンジック です。
F(n); // RAT 起動
fs.unlink(__filename, (x => {})); // setup.js を削除
fs.unlink("package.json", (x => {})); // 悪意ある manifest を削除
fs.rename("package.md", "package.json", // 事前に用意したクリーンな
(x => {})); // スタブと差し替え事後に node_modules/plain-crypto-js/ を覗いても、完全にクリーンな v4.2.0 のマニフェストしか残っていません。証拠が現場で塗り替えられているわけです。
ただし逆に言えば、node_modules/plain-crypto-js/ ディレクトリそのものの存在が、感染の決定的な痕跡になります。正規の axios はこのパッケージに依存していないからです。
なぜ突破されたのか — OIDC バイパス
axios 1.x の正規リリースは通常、GitHub Actions から npm の OIDC Trusted Publisher で公開されています。CI ワークフローと暗号的に紐づくため、本来なら不正公開は困難です。
しかし攻撃者は、メンテナーから盗んだ npm アクセストークンで手動公開することで、OIDC を回避しました。
"_npmUser": {
"name": "GitHub Actions",
"trustedPublisher": { "id": "github", "oidcConfigId": "..." }
}"_npmUser": {
"name": "jasonsaayman",
"email": "ifstap@proton.me"
// trustedPublisher なし、gitHead なし
}GitHub リポジトリ側には 1.14.1 や 0.30.4 に対応するコミット・タグ・リリースは存在しません。npm レジストリ上にしか存在しない幽霊バージョンだったわけです。
自分のプロジェクトは大丈夫か
公開期間は約2〜3時間と短かったものの、その間に npm install を実行した CI/CD パイプラインや開発環境は影響を受けた可能性があります。今すぐ確認しましょう。
1. lockfile でバージョンを確認
# npm
grep -A1 "axios" package-lock.json | grep "version"
# yarn
grep -A1 "axios@" yarn.lock
# pnpm
grep "axios" pnpm-lock.yaml | grep "version"1.14.1 または 0.30.4 が含まれていたら アウト です。
2. plain-crypto-js の存在を確認
# node_modules を直接確認
ls node_modules/plain-crypto-js 2>/dev/null \
&& echo "危険: 感染の可能性あり" \
|| echo "OK: 痕跡なし"
# lockfile も念のため確認
grep "plain-crypto-js" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null繰り返しますが、正規の axios はこのパッケージに依存しません。1ファイルでも存在していたら侵害の証拠です。
3. C2 通信の痕跡を確認
ネットワーク監視ログがあれば、sfrclak[.]com または IP 142.11.206.73 への通信履歴を検索してください。CI/CD のジョブログも対象です。
影響があった場合の対処
安全なバージョンに固定
# 1.x 系
npm install axios@1.14.0
# 0.x 系(レガシー)
npm install axios@0.30.3
# node_modules を完全に再構築
rm -rf node_modules package-lock.json
npm installpackage.json には overrides(npm)/ resolutions(yarn)でピン留めしておくと、推移的依存関係としても引き込まれません。
{
"overrides": {
"axios": "1.14.0",
"plain-crypto-js": "npm:noop@1.0.0"
}
}CI/CD で影響があった場合
ビルド環境にマルウェアが落ちた可能性があります。必ず以下も実施してください。
- npm トークン(特に
NPM_TOKEN)の無効化と再発行 .env、GitHub Actions Secrets、AWS / GCP / Azure 認証情報のローテーション- SSH 鍵、デプロイキー、API キーの再発行
- ビルド成果物の再検証(マルウェアが混入していないか)
- ランナーが共有環境ならば、既知の正常状態から再構築
「現場でクリーンアップ」は推奨しません。マルウェア側が証拠を塗り替えている以上、感染前の状態に戻す方が安全です。
今後の防御策
lockfile を厳密に管理する
package-lock.json / yarn.lock / pnpm-lock.yaml は 必ず Git にコミット してください。CI では npm install ではなく npm ci を使います。
# CI ではこちらを使う
npm cinpm ci は lockfile と一致しない依存関係を エラーで拒否 します。攻撃者が悪意ある推移的依存を追加しても、lockfile が更新されていない限り入りません。
postinstall を無効化する
最強の防御は、そもそも postinstall を実行しない ことです。
ignore-scripts=trueまたは CI で個別指定。
npm ci --ignore-scriptsただし、esbuild / sharp / puppeteer など正当な目的で postinstall を使うパッケージもあります。プロジェクトごとに「どのパッケージは例外的にスクリプトを許可するか」を整理する必要があります。
npm provenance を活用する
npm v9.5 以降では、パッケージの来歴(provenance)を検証できます。
npm audit signaturesGitHub Actions OIDC から公開されたパッケージには署名が付与されます。侵害アカウントからの手動公開には署名がないため、この検証は今回のような攻撃に有効です。
サプライチェーン特化のスキャナを導入
| ツール | 特徴 |
|---|---|
| Socket | サプライチェーン攻撃に特化。今回も6分で検知 |
| Snyk | 脆弱性 DB が充実。CI 統合が容易 |
| Aikido | npm の異常公開検知に強い |
npm audit | 標準ツール。最低限これは実行 |
npm audit だけでは今回のような 公開直後のゼロデイ的サプライチェーン攻撃 には間に合いません。Socket などのリアルタイム検知ツールを CI に組み込んでおくと安心です。
Dependabot / Renovate で自動更新 PR
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10更新 PR を「すぐマージ」せず、24〜48時間寝かせる 運用にしておくと、npm 側で侵害版が引き上げられた後で安全に取り込めます。
GitHub Actions は SHA で固定
タグ(@v4)は上書き可能ですが、コミット SHA は不変です。
# NG: タグは上書き可能
- uses: actions/setup-node@v4
# OK: SHA でピン留め
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde8ef3282e2bf6f1 # v4.2.0これは今回の axios 事件ではなく、その1週間前に起きた PyPI の litellm 侵害事件(GitHub Actions 内で未固定の Trivy が踏み台に使われた)の教訓です。
このブログへの影響
このブログ(blog.printemps.tokyo)の package.json を確認したところ、axios は直接依存にも推移的依存にも含まれていませんでした。
$ npm ls axios
blog.printemps.tokyo@0.1.0
└── (empty)
$ ls node_modules/plain-crypto-js 2>/dev/null
(出力なし → OK)Next.js 製で fetch API を使っているため、axios が入る余地がありませんでした。今回はたまたまセーフでしたが、過去に書いた React Server Components の RCE 脆弱性 React2Shell や その実被害事例 のような事例もあるので、油断はできません。
まとめ — 今回の事件から得られる教訓
- メンテナーアカウントの侵害は防ぎようがない。依存先を「信頼する」だけでは不十分
npm install一発で RAT が入る。postinstall は最強の攻撃ベクター- 証拠隠滅まで自動化されている。事後調査では発見が極めて困難
- lockfile の厳密管理 +
npm ciが最低限の防御ライン - Socket 等のサプライチェーン特化スキャナは実際に6分で検知した実績あり
- OIDC Trusted Publisher の有無で正規・不正の見分けがつく
- 「有名なパッケージだから安全」という前提はもう成り立たない
axios の前週には PyPI の litellm も侵害されました。1週間で2つのメジャーパッケージです。これはもう個別のインシデントではなく、OSS サプライチェーン全体の構造的な脆弱性が露呈している状態だと考えるべきでしょう。
「明日対応しよう」ではなく、今すぐ 自分のプロジェクトの lockfile と CI 設定を見直してください。
参考リンク
- Mitigating the Axios npm supply chain compromise - Microsoft Security Blog
- Axios NPMパッケージ侵害 - トレンドマイクロ
- Supply Chain Compromise Impacts Axios Node Package Manager - CISA
- North Korea-Nexus Threat Actor Compromises Widely Used Axios NPM Package - Google Cloud Blog
- Supply Chain Compromise of axios npm Package - Huntress
- Post Mortem: axios npm supply chain compromise - GitHub Issue #10636
- axios乗っ取り事件の全容 — 39分間で何が起きたか - Qiita