世界を動かす技術を、日本語で。

AxiosがNPMで妥協 – 悪意のあるバージョンがリモートアクセス型トロイの木馬を配布

概要

  • 2026年3月31日、StepSecurityがnpmで配布された axios@1.14.1 および axios@0.30.4悪意あるバージョン を発見。
  • 攻撃者は メンテナのnpm認証情報を乗っ取り、通常のCI/CDをバイパスして 手動で公開
  • 悪意ある依存パッケージ plain-crypto-js@4.2.1 を注入し、 クロスプラットフォームRAT を配布。
  • macOS/Windows/Linux 向けにペイロードを分岐・自動削除で痕跡隠蔽。
  • npmパッケージサプライチェーン攻撃として 極めて高度な手口

axios悪意あるバージョン流通事件の全貌

  • 2026年3月31日、 StepSecurityaxios@1.14.1 および axios@0.30.4悪意あるバージョン をnpmで発見。

  • 攻撃者は メンテナ(jasonsaayman)のnpmアカウント を乗っ取り、 ProtonMail アドレスにメール変更後、 npm CLI で手動公開。

  • GitHub ActionsのCI/CDパイプライン をバイパスし、公式リリースと見分けがつかない状態で公開。

  • plain-crypto-js@4.2.1 を新規依存として追加、 postinstallスクリプトRATドロッパー を実行。

  • plain-crypto-js は実際のaxiosコードで一切import/requireされず、 依存関係のみで発動

    • plain-crypto-js@4.2.0 はクリーンなダミーとして事前公開し、 履歴偽装
    • plain-crypto-js@4.2.1postinstall: "node setup.js"難読化ドロッパー を仕込む。
  • RATドロッパーは C2サーバ(sfrclak.com:8000/6202033) に接続し、 OS別の2段階ペイロード を配布。

  • 実行後は 自己削除 し、 package.jsonをクリーンなダミーで上書き、痕跡を消去。

攻撃タイムライン

  • 2026-03-30 05:57 UTC
    • plain-crypto-js@4.2.0 公開(履歴作成用ダミー)
  • 2026-03-30 23:59 UTC
    • plain-crypto-js@4.2.1 公開(悪意あるpostinstall追加)
  • 2026-03-31 00:21 UTC
    • axios@1.14.1 公開(1.x系ユーザ狙い)
  • 2026-03-31 01:00 UTC
    • axios@0.30.4 公開(0.x系ユーザ狙い、39分差で両系統カバー)

攻撃手法詳細

  • メンテナアカウント乗っ取り

    • jasonsaayman アカウントのnpmトークンを奪取し、手動で公開。
    • GitHub Actions経由でない リリースは通常と異なり、 trustedPublisherやgitHead情報が欠落
    • npmレジストリのメタデータで 不審リリース を識別可能。
  • 悪意ある依存パッケージの事前準備

    • plain-crypto-jscrypto-js の偽装として公開、説明・著者・リポジトリも本物そっくり。
    • postinstallフック証拠隠滅用ファイル(package.md) を仕込み。
  • 依存注入による攻撃

    • axios@1.14.1/0.30.4plain-crypto-js@^4.2.1 を追加した以外、他の依存は一切変更なし。
    • npm install時に自動で plain-crypto-js がインストールされ、 postinstallスクリプト が実行。
  • phantom dependency(幽霊依存)

    • axios 本体コード内で plain-crypto-js は一切使われていない。
    • 依存関係にのみ現れる不自然なパッケージ は、サプライチェーン攻撃の強力な兆候。

RATドロッパーの技術的分析

  • setup.js二重難読化 された1ファイル構成。
  • 文字列・コマンド・C2 URL等は 配列stq[] にエンコード保存、 復号関数_trans_1/_trans_2 で実行時解読。
  • C2 URL: http://sfrclak.com:8000/6202033

OS別ペイロード分岐

  • macOS(darwin)
    • AppleScript を一時ファイルで生成・実行し、 C2からバイナリ取得→/Library/Caches/com.apple.act.mondに配置・実行
    • Appleのシステムキャッシュ風パス で隠蔽、実行後はスクリプト自体を削除。
  • Windows(win32)
    • PowerShellバイナリ を偽装名でコピー、 VBScript で完全非表示のコマンド実行。
    • C2からPowerShell RATスクリプト取得→一時ファイルで実行・自己削除
  • Linux
    • curl でC2からPythonスクリプトを取得、 /tmp/ld.py に保存・実行、後始末も自動化。

axiosユーザへの注意喚起と推奨対応

  • axios@1.14.1 または axios@0.30.4 をインストールした場合、 システム侵害済みと見なすべき
  • 即時のパッケージアップグレード・システムスキャン・証拠保全 が必要。
  • 依存関係の不審な追加・postinstallの有無 を常時監査する体制の重要性。

npmサプライチェーン攻撃の教訓

  • npmパッケージの信頼性はメンテナアカウントのセキュリティに依存
  • OIDC Trusted Publisher等のCI/CD署名 を活用し、 手動公開・不審なメタデータ の検知体制構築。
  • phantom dependency(幽霊依存)postinstallフック の有無は、攻撃兆候の強力なシグナル。
  • サプライチェーン攻撃 は今後も増加傾向、 自動監査・AIアナリストの導入 が有効。

参考元:StepSecurity AI Package Analyst, Harden-Runner

Hackerたちの意見

前回のLiteLLMの事件で認証情報が盗まれたって賭けてもいいよ。いつになったら、こういうパッケージマネージャーを使うのをやめるべきなんだろう?最近、PythonやNode.jsを使うのがちょっと怖いって思ってるけど、これは本当に普遍的な問題だよね。

最近、PythonやNode.jsを使うのがちょっと怖いって思ってるけど。 まさにその通りだよ。Minパッケージの年齢(uvやすべてのJSパッケージマネージャーでサポートされている)はいいけど、今は依存関係をアップグレードしたり新しいプロジェクトを始めるのにすごく躊躇してる。これがすぐに安定するとは思えないから、潜在的に侵害された依存関係をどう扱うかを考える必要があるよね。

Axiosが侵害された影響の規模なんて想像もつかないよ。ほとんどのプロジェクトが、なぜかfetchの代わりに使ってるし(その理由は全然わからない)。報告書からも: > 悪意のあるバージョンには、axios自体に悪意のあるコードは一行も含まれていません。代わりに、偽の依存関係であるplain-crypto-js@4.2.1を注入します。このパッケージはaxiosのソース内でどこにもインポートされておらず、その唯一の目的は、クロスプラットフォームのリモートアクセス型トロイの木馬(RAT)を展開するpostinstallスクリプトを実行することです。pnpmやbunのユーザーには、手動でpostinstallスクリプトを承認しなければならないという良いニュースだね。

ほとんどのプロジェクトが、なぜかfetchの代わりに使ってるし(その理由は全然わからない)。 FetchはNode.jsのコアパッケージとしてバージョン18まで追加されなかったし、バージョン21まで安定版とは見なされてなかったんだ。Axiosはずっと前からあって、人気のフレームワークやチュートリアルの一部になっているから、その使用が広がり続けているんだよね。

pnpmは、遷移的な依存関係でもpostinstallをブロックするの?それともトップレベルだけ?仕事で設定してるけど、サブ依存関係から引っ張られたパッケージのスクリプトがキャッチされるかどうかは実際に試したことがないんだ。

pnpmやbunのユーザーには、手動でpostinstallスクリプトを承認しなければならないという良いニュースだね。 それって、以前のバージョンでは承認してなかったの?でも、そんなに広く使われているプロジェクトだから、自動承認の可能性も高いんじゃない?

PSA: npm/bun/pnpm/uvは、パッケージの最小リリース年齢を設定することを今サポートしてるよ。私の~/.npmrcにはignore-scripts=trueも入れてる。分析に基づくと、それだけで脆弱性を軽減できたはず。bunとpnpmはデフォルトでライフサイクルスクリプトを実行しないよ。グローバル設定で最小リリース年齢を7日間に設定する方法はこれだよ: ~/.config/uv/uv.toml exclude-newer = "7 days" ~/.npmrc min-release-age=7 # days ignore-scripts=true ~/Library/Preferences/pnpm/rc minimum-release-age=10080 # minutes ~/.bunfig.toml [install] minimumReleaseAge = 604800 # seconds (余談だけど、npm、bun、pnpmがこの設定に異なる時間単位を使うことに決めたのはすごいよね。)LLMエージェントで開発しているなら、この設定から生じる失敗をどう扱うかについて、AGENTS.md/CLAUDE.mdファイルも更新した方がいいよ。そうしないと、エージェントが無駄に時間を浪費しちゃうから。

yarn berryの場合は、~/.yarnrc.ymlにnpmMinimalAgeGate: "3d"を追加してね。

みんなが過去7日以内にリリースされたパッケージを避ければ、悪意のあるコードは7日間はおとなしくしてる可能性が高いよ。

これが人気がないかもしれないけど、こういうことを遅らせるために必要な人間のアクションやタイミングを設定する場所はないのかな?例えば、GHアクションでデプロイするためには最終的な人間のクリックが必要で、それをCLIに変更するには3日間のクールダウン期間を設けて、必須のセキュリティメールを送信するとか。メール変更後は6時間読み取り専用にするとか。これらのアイデアには穴があるけど、基本的な考え方はセキュリティを物理的なセキュリティのように扱うことだよ。目標は常に100%ブロックすることではなく、攻撃者をxxx分遅らせて、チームが何が起こっているのかを把握する時間を与えることなんだ。

こんにちは、セキュリティ担当です。試みたけど、このステップに必要な人数と、レビューや大きなボタンをクリックするためにいる人数のバランスがいつもボトルネックになるんだ。だから、このステップは削除されることになるよ。むしろ、使うバージョンを固定して、リリース後に意図的にアップデートする方がずっと良いアプローチだと思う。例えば、スプリントの後にね。

最低リリース年齢は最悪だけど、前にもこんなことがあったよね。メールの添付ファイルも最初は無法地帯だったし、みんながクアランティンの遅延やファイルブロック、その他の摩擦を追加して、なんとか動くようになった。今回は、チョークポイントが少なくて、実行が期待の一部として自然に感じるから、もっと悪化してる気がする。結局、インストールがめちゃくちゃ複雑になるのは確実だね。もう解決策が見えてきてるし… クーリング期間、メンテナープロファイリング、サンドボックスの爆発、ロックファイルの差分確認、変な公開パスのチェック。全部合わせると、簡単な開発には大きな手間がかかるよ。

最小リリース年齢は、こういう非自明なケースでは脆弱性の適用を数日遅らせるだけかもしれないね。考えれば考えるほど、Odin言語のパッケージマネージャーなしのアプローチは納得できる。ただ、そのアプローチはJavaScriptには合わない。ちょっとしたことでもnpmパッケージが必要だからね。ゴーランのようなベンダリングアプローチも、JavaScriptの依存関係の多さと変動の激しさでは通用しないと思う。

本当に、どうやって自分のシステムにあるソフトウェアがこれを引き込まないようにするの?こういうことがあるから、パスワードマネージャーをソフトウェアのコンパイルと同じ環境に置きたくないから、Qubesに永遠に移行したくなるんだよね。

サプライチェーン攻撃は本当に怖いから、ほとんどの企業はこれらのコアライブラリの独自バージョンをハードフォークするエージェントを使うようになると思う。以前は実用的じゃなかったけど、今は確実にやりやすくなったね。

最近はスキャナーがたくさんあるから、こういうのはすぐに見つかるよね。npmか誰かが、これらのスキャナーを通過したパッケージだけを通すレジストリを持つ必要があると思う。複数のスキャナーによるレポートを集約するvirustotalみたいなこともできるし。NPMは信頼できるビルド環境の証明書を発行してるし、Googleにはoss-rebuildがある。レジストリを切り替えるのはnpm config setだけで済むしね。大変なのは、いろんなセキュリティ会社が協力するように説得できる中央の組織がいることだと思う。各社がそれぞれ異なるレジストリを持つんじゃなくてね。単なるハードコーディングされた遅延じゃなくて、どのチェックを最初に通過しなきゃいけないかのポリシーを持つ方が、CVEが出たときのオーバーライドも含めて理にかなってると思う。(進行中)

もう同じことを繰り返すのは嫌だけど、依存関係でこれを何度も見るんだ。毎回、Rustでも同じことが起こるんじゃないかと心配になる。太い標準ライブラリのアプローチがうまくいかないのは分かってるけど、安全で高品質なパッケージを信頼できる良い解決策が欲しいんだよね。

キュレーションされた依存関係をホスティングするのは、商業的に価値のあるサービスだよね。最終的には、人々がベンダーにパッケージを審査してもらうためにお金を払う経済が生まれるんだ。

JSのパッケージマネージャー(pnpm、bun)は、デフォルトでpostinstallスクリプトを無視するようになったよ。npmだけは、レガシーの理由でまだ実行するけどね。多分、デフォルトでこれらのスクリプトを実行しないように設定した方がいいよ。ほとんど必要ないから。~/.npmrc : ignore-scripts=true