脆弱性・インシデント

axiosにマルウェア混入(1.14.1・0.30.4)|感染確認・対処と再発防止の設定

axiosにマルウェア混入(1.14.1・0.30.4)|感染確認・対処と再発防止の設定

2026年3月31日、npmの定番HTTPクライアントaxiosで、メンテナのアカウントから不正版 1.14.1 と 0.30.4 が公開されました。どちらも [email protected] という依存を追加しただけの版で、npm install の時点でmacOS・Windows・Linux向けのRAT(遠隔操作型マルウェア)を取り込みます。不正版がnpmにあったのは約3時間で、同日中に削除されています。

この記事では、axios公式のポストモーテム(事後報告)とGoogle脅威インテリジェンスグループ(GTIG)の分析をもとに、経緯・手口・自分の環境の確認方法・感染時の対処を整理します。掲載した痕跡確認のスクリプトは、WindowsのGit BashとPowerShellで動作を確かめた版です。そのうえで、事件後にnpm・pnpm・Yarnの既定値がどう変わったか、いま入れるべきaxiosの版はどれかまで扱います。

まとめ:axiosマルウェア混入の要点と今すぐ確認すること

  • 対象は2版だけ:[email protected] と [email protected]。公開は2026年3月31日 00:21〜03:15 UTC(日本時間 09:21〜12:15)です。この時間帯に依存を解決した環境に加え、そのとき不正版が入ったlockfileやパッケージキャッシュを後から使った環境も対象になります。
  • 確認はlockfileの検索から:package-lock.json などに 1.14.1・0.30.4・plain-crypto-js が出るか。感染後は npm ls が [email protected] と表示するよう偽装されるので、版番号でなくディレクトリの有無で判断します。
  • Windowsの痕跡はPowerShellで見る:Git Bashから $PROGRAMDATA を参照すると空になり、何も調べずに「痕跡なし」と出るスクリプトになりがちです。本記事のスクリプトは確認できなかった項目を「未確認」として返します。
  • 見つかったら端末ごと侵害扱い:GitHubの勧告(GHSA-fw8c-xr5c-95f9)は「完全に侵害されたとみなし、別の端末から全ての秘密情報を失効させる」よう求めています。削除だけでは不十分です。
  • 攻撃者は北朝鮮系のUNC1069とGTIGが帰属しています。入口はコードではなく、メンテナ個人へのソーシャルエンジニアリングでした。
  • いま入れる版は1.20.0(0.x系は0.34.0)。事件直後の案内にあった1.14.0は、その後に公表された脆弱性が未修正です。
  • 再発防止は公開待ち期間とinstall scriptsの遮断:npm 12は依存のinstall scriptsを既定で止め、pnpm 11とYarn 4.15は公開から1日経っていない版を既定で入れません。npm 11のままなら min-release-age を自分で設定します。

axiosマルウェア混入の経緯(2026年3月30日〜31日)

npm公開時刻に基づく時系列:axios不正版の公開から削除まで約3時間

npmレジストリの公開時刻、axios公式ポストモーテム、Datadog Security Labsの時系列を突き合わせると、次の流れになります。攻撃者は前日に中身が無害な偽装版の [email protected] を公開して「公開履歴のない新規パッケージ」に見えないよう下準備し、約18時間後に悪性の4.2.1を出しています。

日時(UTC) 日本時間 出来事
3月30日 05:57 3月30日 14:57 [email protected]偽装公開
3月30日 23:59 3月31日 08:59 [email protected]悪性公開
3月31日 00:21 3月31日 09:21 [email protected] 公開
3月31日 01:00 3月31日 10:00 [email protected] 公開・外部の検知が始まる
3月31日 01:38 3月31日 10:38 共同メンテナが非推奨化PRを出しnpmへ連絡
3月31日 03:15 3月31日 12:15 axios不正版をnpmから削除・GHSA公開
3月31日 03:29 3月31日 12:29 plain-crypto-jsをnpmから削除

4.2.0の公開時刻はポストモーテムに、4.2.1の公開時刻はDatadogの時系列に載っています。plain-crypto-jsはレジストリから削除済みのため、npm上の公開記録は直接は確認できません。ポストモーテムによると、利用者の報告Issueは乗っ取られたアカウントに削除され、共同メンテナのDigitalBrainJS氏がnpmへ直接連絡して削除に至りました。GTIGは攻撃期間を00:21〜03:20 UTCとしています。

侵入経路:メンテナ個人へのソーシャルエンジニアリングと偽の更新

主任メンテナのJason Saayman(GitHub: jasonsaayman)氏が2026年4月2日に公開したポストモーテムでは、侵入経路を「標的型のソーシャルエンジニアリングとRATによるPCへの侵入」と説明しています。Datadog Security Labsがまとめた同氏の説明では、攻撃者は実在企業の創業者になりすまし、偽のSlackワークスペースへ招待したうえでMicrosoft Teams会議を設定し、「システムのコンポーネントが古い」と言って更新を促しました。その「更新」がRATでした。

npmのレジストリ情報にも痕跡が残っています。正規の1.13.6と1.14.0は公開者が GitHub Actions(OIDCによるTrusted Publishing)で、SLSAの来歴証明(provenance)が付いています。1.14.1は個人の認証情報で手動公開され、この紐付けがありませんでした。0.x系は事件前から個人アカウントで公開されており(0.30.3も同様)、この違いは出ません。axiosはv1.15.0で長期トークン NODE_AUTH_TOKEN を廃止し、公開をOIDCに一本化しています。

攻撃者の分析:GTIGが北朝鮮系UNC1069に帰属させた根拠

GTIGは2026年3月31日の分析で、この攻撃を2018年から活動する金銭目的の北朝鮮系アクター UNC1069 に帰属させました。根拠は2つあります。C2サーバーへの接続元に、過去にUNC1069が使ったAstrillVPNのノードがあったこと。そして投下されたバックドア WAVESHAPER.V2 が、UNC1069のものとされてきたWAVESHAPERの改良版だったことです。

GTIGは週間ダウンロード数を1.x系で1億回超、0.x系で8,300万回と記しています。axiosに依存する別パッケージ経由でも入るため、axiosを直接使っていないプロジェクトも対象です。法執行機関による発表やnpm側の公式な事後報告は、2026年10月2日時点で確認できていません。

plain-crypto-jsとWAVESHAPER.V2の手口

postinstallで起動し、実行後の痕跡を差し替えるドロッパー

不正版のaxios本体のコードは正規版と同じで、変わったのは package.json の依存に plain-crypto-js が1行増えたことだけです。[email protected] は "postinstall": "node setup.js" を持ち、インストール直後に難読化されたドロッパーが動きます。アプリがaxiosをimportしなくても、インストールした時点で実行されます。

ドロッパーは実行後に setup.js を削除し、package.json をpostinstallの無い版に差し替えます。StepSecurityの解析では、差し替え後の package.json は版番号が4.2.0なので、npm list plain-crypto-js を実行すると [email protected] と表示されます。「4.2.1が無いから安全」という判断では、この偽装を見逃すおそれがあるため注意が必要です。正規のaxiosはどの版もplain-crypto-jsに依存していないので、ディレクトリがあれば不正版を取り込んだ証拠です。install scriptsを止めた設定(後述)で入れていない限り、ドロッパーも実行されたとみなします。

OS別のペイロード設置先とバックドアの情報送信・遠隔実行の機能

ドロッパーはOSを判定し、C2サーバー sfrclak[.]com(142.11.206.73、ポート8000)から第2段を取得します。GTIG・Datadog・StepSecurityの解析で共通する設置先は次のとおりです。表のmacOSのファイルは /Library/Caches/com.apple.act.mond、Windowsの2ファイルは %PROGRAMDATA%\wt.exe と %PROGRAMDATA%\system.bat を指します。

OS 主な痕跡 第2段の形式
macOS com.apple.act.mond C++のMach-Oバイナリ
Windows wt.exe・system.bat PowerShell
Linux /tmp/ld.py Pythonスクリプト

Windowsの wt.exe はWindows Terminalを装ったPowerShellのコピーです。GTIGによると、ログオン時に起動させるため HKCU\Software\Microsoft\Windows\CurrentVersion\Run に MicrosoftUpdate という名前のエントリも作ります。WAVESHAPER.V2は60秒間隔でC2に指示を問い合わせ、ホスト名・ユーザー名・OS版・プロセス一覧の送信、ディレクトリの再帰列挙、任意のスクリプトやバイナリの実行(kill・rundir・runscript・peinject)を行います。端末上の認証情報を読める状態だったと考えるべきです。

開発端末とCIで自分の環境が感染しているかの確認手順とスクリプト

lockfileの検索:1.14.1・0.30.4・plain-crypto-jsの有無

最初に見るのはlockfileです。ポストモーテムが示した確認コマンドは次の1行です。

grep -E "axios@(1\.14\.1|0\.30\.4)|plain-crypto-js" package-lock.json yarn.lock 2>/dev/null

このコマンドはpnpmとBunのlockfileを見ていません。また package-lock.json やYarn 2以降の yarn.lock では版が "version": "1.14.1" や axios@npm:1.14.1 の形で書かれるため、実際に当たるのは plain-crypto-js の行です。そこで、npm・Yarn・pnpm・Bunのlockfileと node_modules 内のディレクトリ、OS別の痕跡をまとめて見るスクリプトを用意しました。

痕跡確認スクリプト(bash)の使い方と終了コード0・1・2の意味

次の内容を check-axios.sh という名前で保存し、プロジェクトの直下で bash check-axios.sh と実行します。端末に直接貼り付けると、最後の exit で端末そのものが閉じるので、必ずファイルに保存してから実行してください。

#!/usr/bin/env bash
# axios 1.14.1 / 0.30.4 と plain-crypto-js の痕跡を探す
# 使い方:check-axios.sh として保存し、プロジェクト直下で「bash check-axios.sh」と実行する
#         (端末に直接貼り付けると、最後の exit で端末が閉じる)
# 終了コード:0=確認した範囲で痕跡なし/1=痕跡あり/2=確認できなかった項目がある
hit=0; unchecked=0
for f in package-lock.json npm-shrinkwrap.json yarn.lock pnpm-lock.yaml bun.lock; do
  [ -f "$f" ] || continue
  if grep -n -E '(^|[^A-Za-z0-9_.-])axios(@npm:|@|-)(1\.14\.1|0\.30\.4)([^0-9]|$)|(^|[^A-Za-z0-9_.-])plain-crypto-js([^A-Za-z0-9_.-]|$)' "$f"; then
    echo "[HIT] $f"; hit=1
  fi
done
[ -f bun.lockb ] && { echo "[未確認] bun.lockb はバイナリのため調べていない"; unchecked=1; }
# 感染後は package.json が 4.2.0 表記に差し替わるので、版番号でなくディレクトリの有無で見る
found=$(find . -type d -name plain-crypto-js -path '*node_modules*' 2>/dev/null)
[ -n "$found" ] && { echo "[HIT] $found"; hit=1; }
for p in /Library/Caches/com.apple.act.mond /tmp/ld.py; do
  [ -e "$p" ] && { echo "[HIT] $p"; hit=1; }
done

# Windows(Git Bash・MSYS2・Cygwin)の判定
# Git Bash では環境変数名が Windows の表記「ProgramData」のまま渡り、$PROGRAMDATA は空になる。
# 空のまま判定すると Windows の痕跡を1つも見ずに「痕跡なし」と出るため、表記ゆれを順に拾い、
# それでも取れなければ「未確認」として扱う
case "$(uname -s)" in
  MINGW*|MSYS*|CYGWIN*)
    pd="${PROGRAMDATA:-${ProgramData:-${ALLUSERSPROFILE:-}}}"
    if [ -z "$pd" ]; then
      echo "[未確認] ProgramData の場所を取得できない(PowerShell の手順で確認する)"; unchecked=1
    else
      command -v cygpath >/dev/null 2>&1 && pd=$(cygpath -u "$pd")
      if [ ! -d "$pd" ]; then
        echo "[未確認] ProgramData($pd)が見つからない"; unchecked=1
      else
        for n in wt.exe system.bat; do
          [ -e "$pd/$n" ] && { echo "[HIT] $pd/$n"; hit=1; }
        done
      fi
    fi
    # Run キーの MicrosoftUpdate。MSYS のパス変換で /v が壊れないよう変換を止めて reg.exe を呼ぶ
    runkey='HKCU\Software\Microsoft\Windows\CurrentVersion\Run'
    if MSYS2_ARG_CONV_EXCL='*' reg.exe query "$runkey" >/dev/null 2>&1; then
      MSYS2_ARG_CONV_EXCL='*' reg.exe query "$runkey" /v MicrosoftUpdate >/dev/null 2>&1 \
        && { echo "[HIT] $runkey の MicrosoftUpdate"; hit=1; }
    else
      echo "[未確認] レジストリの Run キーを読めない(PowerShell の手順で確認する)"; unchecked=1
    fi
    ;;
  Linux)
    if grep -qi microsoft /proc/version 2>/dev/null; then
      echo "[未確認] WSL からは Windows 側の ProgramData とレジストリを見ていない(Windows の PowerShell で確認する)"; unchecked=1
    fi
    ;;
esac

if [ $hit -eq 1 ]; then
  echo "侵害の疑いあり:端末を隔離し、別端末から認証情報を失効させる"; exit 1
elif [ $unchecked -eq 1 ]; then
  echo "痕跡は見つからなかったが、[未確認] の項目が残っている。安全とは判断しない"; exit 2
else
  echo "確認した範囲では痕跡なし"; exit 0
fi

終了コードは3種類です。0は確認した範囲で痕跡なし、1は痕跡あり、2は調べられなかった項目が残っていることを示します。2は安全の意味ではありません。表示された [未確認] の項目を、PowerShellなど別の手段で確認してください。

macOS標準のbash 3.2で、1.14.1を含む package-lock.json・yarn.lock(axios@npm:1.14.1 形式)、0.30.4を含む pnpm-lock.yaml、1.14.1を含む bun.lock、node_modules/plain-crypto-js ディレクトリをそれぞれ用意して実行し、すべて [HIT] と終了コード1になることを確認しました。1.20.0に解決された正常なlockfile、1.14.10、[email protected] のような別の版・別パッケージには反応しません。

Git Bashで$PROGRAMDATAが空になり誤って痕跡なしと出る問題

この記事の旧版に載せていたスクリプトは、Windowsの痕跡を "${PROGRAMDATA:-/nonexistent}/wt.exe" で確認していました。Windows版のGit Bash(Git for Windows同梱のbash 5.3.15で確認)では、環境変数がWindowsの表記どおり ProgramData という大小混在の名前で渡り、$PROGRAMDATA は空です。bashの変数名は大文字と小文字を区別するため、旧版は存在しない /nonexistent/wt.exe だけを調べ、Windowsの痕跡を1つも見ないまま「確認した範囲では痕跡なし」と表示していました。

2026年10月2日に、ダミーの wt.exe を置いたフォルダを ProgramData に指定し、PowerShellからGit Bashを起動して比較しました。旧版は PROGRAMDATA=[] のまま「痕跡なし」で終了コード0、上の修正版は [HIT] と終了コード1でした。修正版は PROGRAMDATA・ProgramData・ALLUSERSPROFILE を順に見て、どれも取れなければ [未確認] にします。Run キーは reg.exe で読みますが、Git Bashは /v のような引数をパスとみなして書き換えるため、MSYS2_ARG_CONV_EXCL='*' で変換を止めています。止めないと reg.exe が構文エラーを返し、その終了コード1が「値なし」と区別できません。

WSLから実行した場合、Windows側のProgramDataとレジストリは見ていないので [未確認] になります(WSL上での実行は今回試していません)。WindowsではこのあとのPowerShell版を正の手順として使ってください。

WindowsはPowerShellでProgramDataとRunキーを確認

Windowsでは、PowerShellで次を実行します。[Environment]::GetFolderPath('CommonApplicationData') は環境変数に頼らずにProgramDataの場所(通常は C:\ProgramData)を返します。

# axios 不正版の RAT 痕跡(Windows)を確認する。PowerShell 5.1 / 7 で動作
$pd = [Environment]::GetFolderPath('CommonApplicationData')  # 通常は C:\ProgramData
$hit = $false
foreach ($n in 'wt.exe', 'system.bat') {
  $p = Join-Path $pd $n
  if (Test-Path -LiteralPath $p) { "[HIT] $p"; $hit = $true }
}
$run = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name 'MicrosoftUpdate' -ErrorAction SilentlyContinue
if ($run) { "[HIT] Run キー MicrosoftUpdate = $($run.MicrosoftUpdate)"; $hit = $true }
if ($hit) { '侵害の疑いあり:端末を隔離し、別端末から認証情報を失効させる' } else { "確認した範囲では痕跡なし(確認先:$pd と Run キー)" }

PowerShellのウィンドウにそのまま貼り付けて実行できます。.ps1 に保存して使う場合は、UTF-8(BOM付き)で保存してください。Windows PowerShell 5.1はBOM無しのUTF-8を既定の文字コードで読むため、日本語の文字列が化けて構文エラーになります(手元のWindows 11・PowerShell 5.1で、BOM無しは構文エラー、BOM付きと貼り付けは正常に動くことを確認)。ダミーの wt.exe と既存のRun値に置き換えた試験でも、両方が [HIT] になりました。

ネットワーク側では、プロキシやDNS、EDRのログで sfrclak[.]com と 142.11.206.73:8000 への通信を探します。

npm ls axiosによる入れ子の依存の洗い出しと不正版の判定

npm ls axios は、自分で入れたaxiosに加えて、他のパッケージ経由で入ったaxiosも木構造で表示します。pnpm why axios・yarn why axios も同じ用途で使えるコマンドです。ここで1.14.1か0.30.4が出たら侵害の疑いがあります。

plain-crypto-jsについては、先に書いたとおり npm ls の表示は当てになりません。npm ls plain-crypto-js が4.2.0と答えても、差し替え後の偽装である可能性があり、安全の根拠になりません。判定には ls node_modules/plain-crypto-js でディレクトリの有無を見ます。

CIランナーのジョブ履歴と共有キャッシュの確認による影響範囲の判定

lockfileにヒットしなくても、CIのように毎回lockfileなしで依存を解決する環境では、その時刻に取り込んでいた可能性が残ります。バイナリ形式の bun.lockb と過去のジョブの実行履歴は、上のスクリプトでは調べられません。

CIランナーでは、3月31日 00:21〜03:15 UTCに npm install などで依存を新しく解決したジョブが第一の対象です。その時間帯に作られたlockfileや、共有キャッシュに残った不正版を後日のジョブが使った場合も対象に含めます。不正版を含まないlockfileを npm ci で固定していたジョブは影響を受けません。ポストモーテムも「クリーンな版に固定済みで、この時間帯に新規インストールをしていなければ問題ない」としています。GitHubホストランナーは使い捨てなので端末の汚染は残りませんが、そのジョブに渡したシークレットは漏れた前提で扱います。

感染していた開発端末とCIの対処手順:隔離・認証情報の失効・再構築

痕跡が見つかったら、端末を作り直す前提で動きます。GitHubの勧告は、パッケージを消してもマルウェアが全て消える保証はないと明記しています。

  1. 端末をネットワークから切り離す。C2との通信を止め、追加の命令を受け取らせないためです。
  2. 別の端末から認証情報を失効・再発行する。npmトークン、GitHubのトークンとSSH鍵、クラウドのアクセスキー、.env に置いたAPIキー、DBのパスワードが対象です。感染端末上で作業すると、新しい値も盗まれます。
  3. CIで該当ジョブが走っていたら、そのジョブに注入したシークレットを全てローテーションする。
  4. 端末を既知の正常な状態から再構築する。トレンドマイクロもその場でのクリーンアップを避けるよう勧めています。
  5. npm・Yarn・pnpmのキャッシュを消してから入れ直す。GTIGは、共有キャッシュから再感染しないよう、開発端末とビルドサーバーの両方でキャッシュを削除するよう求めています。

クラウドのアクセスキーは失効を先に済ませ、そのあとで監査ログ(AWSならCloudTrail)から3月31日以降の不審な利用を洗い出して、被害の範囲を調べます。自社のビルド環境や配布物に同じ種類の穴が無いかを第三者に見てもらう場合は、脆弱性診断・セキュリティ診断で依存関係とCIの設定まで含めて確認できます。

更新先の版は1.14.0でなく1.20.0・0.34.0(2026年10月2日時点)

ポストモーテムは事件当日の緊急対応として、当時最新の正規版だった1.14.0(0.x系は0.30.3)へ戻すよう案内しました。2026年10月の時点でこの案内に従うのは誤りです。

axiosのGitHub Security Advisoriesには、事件後の2026年4月から9月16日までに47件の脆弱性情報が公表されました(4月24日に13件、7月6日に10件、9月16日に12件など)。2026年10月2日に一覧を取り直した時点で、9月16日より後の公表はありません。影響する版の範囲を1件ずつ照合すると、1.14.0はこのうち39件(high 13件)、0.30.3は24件(high 8件)の範囲に入ります。9月16日の12件は、1.x系の修正が全て1.20.0以降で、0.x系にも影響する件の修正は0.34.0以降です。

いま入れる版は 1.20.0(2026年8月26日公開、2026年10月2日時点の latest)、0.x系を使い続ける場合は 0.34.0(2026年9月16日公開、同 v0x)です。版の確認と更新の手順はaxiosの最新版1.20の確認方法と使い方に、CVE-2026-40175の中身はCVE-2026-40175(axios)の解説にまとめています。

開発端末とCIで再発を防ぐパッケージマネージャーの設定と適用条件

公開待ち期間(cooldown)の設定と修正版が遅れる副作用

不正版がnpmにあったのは約3時間でした。公開から一定期間が経っていない版を候補から外しておけば、解決の時点で不正版は選ばれません。pnpmもv10.16.0の変更履歴で「多くの場合、悪性版は1時間以内に発見・削除される」と導入理由を書いています。

ツール 設定名 単位 既定値
npm(11.10.0〜) min-release-age 日 なし(npm 12.1.0でも同じ)
pnpm(10.16.0〜) minimumReleaseAge 分 1440(11.0.0から)
Yarn(4.10.0〜) npmMinimalAgeGate 1d・3h等 1d(4.15.0から)
Bun minimumReleaseAge 秒 なし
Dependabot cooldown 日 3日(GitHub.com)

npmの min-release-age は事件の時点で使えましたが、npm 12の設定ドキュメントでも既定は無効のままです。.npmrc に自分で書きます。pnpmの値はpnpmの依存解決の設定に載っています。

# .npmrc
min-release-age=7
# 社内パッケージなど、すぐ更新したいものだけ除外する
min-release-age-exclude=@your-org/*

npm 11.17.0で "axios": "^1.14.0" を min-release-age=40 で解決すると、2026年9月27日時点では公開32日目の1.20.0が除外され、1.19.0 に解決されました。1.19.0は9月16日公表の12件が未修正の版です。期間を200日にすると条件を満たす版が無くなり、notarget エラーで止まります。

期間を長くするほど、脆弱性の修正版も同じだけ遅れて入ります。版を明示しても待ち期間は回避できず、"axios": "1.20.0" と書いて min-release-age=40 で解決すると ETARGET で止まりました。期間は7日程度に留め、緊急の修正版は min-release-age-exclude にそのパッケージを一時的に加えたうえで版を指定して入れます。Dependabotのcooldownはバージョン更新だけに効き、セキュリティ更新には効かないので、修正版のPRは遅れません。

依存パッケージのinstall scriptsの遮断と実行許可の設定

今回の感染はpostinstallの実行で起きました。依存パッケージのinstall scriptsを止めれば、plain-crypto-jsが入っても setup.js は動きません。事件後、各ツールの既定値はこの方向に変わっています。

  • npm 12.0.0(2026年7月8日):依存のinstall scriptsを既定でブロック。許可は npm install-scripts approve <pkg> で package.json の allowScripts に記録します。npm 11系は既定で実行するので、ignore-scripts=true が必要です。
  • pnpm:10.0.0から既定で実行しません。pnpm 11.15.1でpostinstall付きの依存を入れると、スクリプトは実行されず、ERR_PNPM_IGNORED_BUILDS で終了コード1になりました。許可は pnpm approve-builds か allowBuilds で行います。
  • Yarn 4.14.0(2026年4月16日):enableScripts: false が既定になりました。
  • Bun:依存のlifecycle scriptsを既定で実行せず、trustedDependencies か既定の許可リストに載ったパッケージだけを実行します。

npm 11.17.0で同じ依存を入れると、既定ではpostinstallが実行され、--ignore-scripts を付けると実行されないことを確認しました。install scriptsでネイティブモジュールを用意するパッケージは、止めると動かなくなります(npmのドキュメントは許可の例に canvas と sharp を挙げています)。全面的に無効にするより、必要なパッケージだけを許可リストに載せる方式が、npm 12・pnpm・Bunに共通する考え方です。

pnpmのtrustPolicyによる公開経路の信頼度低下の検知

pnpm 10.21.0で追加された trustPolicy: no-downgrade は、それまでTrusted Publisherで公開されていたパッケージの新しい版が、来歴証明なし・信頼性の低い経路で公開されたときにインストールを失敗させます。先に見たとおり、axios 1.x系は1.14.0までGitHub ActionsのOIDCで公開され、1.14.1だけがその紐付けを欠いていました。これは、この設定が検知の対象とする変化です。

pnpmのドキュメントによると、比較はsemverでなく公開日時の順で行われ、それより前に公開されたどれか1つの版が強い信頼の証拠を持っていれば、弱い版は入れられません。0.30.4は1.14.0より後に個人の認証情報で公開されたため、同じ理由で弾かれる側に入ります。

# pnpm-workspace.yaml
minimumReleaseAge: 1440
trustPolicy: no-downgrade

一方で、もともとTrusted Publishingを使っていないパッケージでは信頼度の低下が起きないため、この設定は何も検知しません。cooldownとinstall scriptsの遮断を先に入れ、trustPolicyは追加の網として使います。pnpmの設定全般はpnpmの共有ストアの仕組みとv12の解説で扱っています。

メンテナ側の再発防止:長期トークンから承認付きOIDC公開への移行

ポストモーテムは教訓として「個人アカウントから直接公開していたのは避けられたリスクだった」「不正な公開を自動で検知する手段がなく、発見はコミュニティ頼みだった」と書いています。axiosはv1.15.0で公開をOIDCに一本化し、Actionsの参照をコミットSHAで固定し、公開ステップを専用の npm-publish 環境の承認制にしました。

パッケージを公開する側が真似できるのは、長期のnpmトークンを持たないこと、公開をCIのOIDCに限ること、公開ジョブに承認を挟むことの3点です。GitHub Actions側の設定はGitHub ActionsのSecrets管理とOIDC移行とGitHub Immutable Releasesの設定手順が参考になります。ただしImmutable Releasesが守るのはGitHub上のリリースで、npmへの不正な新規公開そのものは防げません。

メンテナの認証情報を奪って不正版を出す攻撃は、2025年以降の自己増殖型Shai-Huludでも繰り返されています。2026年8月4日には、Elastic Security Labsが報告した新たな波(CHAINDROP)で、keyvのメンテナが侵害され、盗まれたnpmの認証情報を使って同じメンテナが公開権限を持つ400超のパッケージに不正版が出されました。手口は今回と同じく、install時に動くスクリプトの追加です。

よくある質問

axiosのマルウェア混入について、確認方法と更新先に関する質問をまとめました。

自分のプロジェクトがaxiosを使っていなくても影響はありますか?

あります。axiosは多くのパッケージの依存に入っているため、間接的に入っている場合があります。npm ls axios で入れ子まで確認し、あわせてlockfileとキャッシュに1.14.1・0.30.4が残っていないかを見てください。3月31日 00:21〜03:15 UTC(日本時間 09:21〜12:15)に依存を解決したかどうかが、最初の手がかりです。

npm ls で [email protected] と表示されたら安全ですか?

安全の根拠にはなりません。マルウェアは実行後に package.json を4.2.0表記の無害なものへ差し替えます。正規のaxiosはplain-crypto-jsに依存しないため、node_modules/plain-crypto-js があれば不正版を取り込んでいます。install scriptsを止めていなかった環境では、ドロッパーが実行されたとみなしてください。

axiosはどのバージョンに更新すればよいですか?

2026年10月2日時点では1.20.0、0.x系は0.34.0です。事件当日に案内された1.14.0と0.30.3にマルウェアは入っていませんが、その後に公表された脆弱性が未修正です。更新後は npm ls axios で入れ子の依存まで新しい版になったかを確かめます。

axiosを攻撃したのは誰ですか?

Googleの脅威インテリジェンスグループ(GTIG)は、北朝鮮系の金銭目的アクターUNC1069に帰属させています。C2への接続元が過去の活動と同じVPNノードだったことと、バックドアが既知のWAVESHAPERの改良版だったことが根拠です。

WindowsでRATの痕跡を確認する方法はありますか?

PowerShellで C:\ProgramData 直下の wt.exe と system.bat の有無を Test-Path で確認し、HKCU:\Software\Microsoft\Windows\CurrentVersion\Run に MicrosoftUpdate というエントリが無いかを見ます。本文のPowerShell版がこの3点をまとめて確認します。Git Bashで自作のスクリプトを使う場合は、$PROGRAMDATA が空になる点に注意してください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.15 テックブログ リゾートトラスト不正アクセス:会員2.6万件・カハラ横浜3.6万件の対象確認と窓口
  2. 2025.09.03 コラム ストループ効果とは?原典1935年の実測値と年齢別の平均・テストのやり方
  3. 2026.01.20 テックブログ PineDramaとは?TikTokショートドラマアプリの料金・安全性とTikTok内ミニドラマの課金
  4. 2026.04.13 コラム ゴルフ賞金の税金はいくら?1,000万円で源泉194万円・確定申告での精算と副賞の車、海外賞金【令和8年分】
  5. 2026.10.15 テックブログ 銀のさら不正アクセス:最大650万件の対象確認と窓口、パスワード変更・退会の手順

RELATED POSTS 関連記事

目次