技術 約5分で読めます

axiosがprototype pollutionのgadgetとしてHTTPヘッダ注入を許していた CVE-2026-40175

いけさん目次

3月末にaxiosのnpmアカウントが乗っ取られ、RATを投下するサプライチェーン攻撃が起きた。
その後の調査で北朝鮮系グループUNC1069によるソーシャルエンジニアリングの手口や、Fastify・Lodashなど他メンテナへの攻撃キャンペーンも明らかになった。

今回のCVE-2026-40175は、それらとは完全に別件だ。
サプライチェーンの話ではなく、axios自体のコードに元から存在した実装上の脆弱性が修正されたという話。

CVE-2026-40175の概要

GitHub AdvisoryとNVDによると、axiosの設定マージ処理が、他ライブラリ由来のprototype pollutionで汚染された Object.prototype 上の値を拾い、HTTPヘッダとしてそのまま使ってしまう。
さらに、そのヘッダ値に含まれるCRLF(\r\n)を十分に検証していなかったため、ヘッダ注入・リクエスト分割・SSRF(Server-Side Request Forgery、サーバ側からの意図しないリクエスト送信)への踏み台にできる。

  • CVSS: 10.0 / 9.9
  • 修正バージョン: 1.15.0

axios単体が突然prototypeを汚染するわけではない。「汚染済みの世界で危険なgadgetとして振る舞う」のが本質だ。

prototype pollutionとは

JavaScriptのオブジェクトはすべて Object.prototype を継承している。
prototype pollutionとは、攻撃者が Object.prototype に任意のプロパティを注入する攻撃手法で、以降に生成されるすべてのオブジェクトがその汚染値を引き継ぐ。

// prototype pollution の例
const malicious = JSON.parse('{"__proto__": {"polluted": true}}');
merge({}, malicious); // 不適切なマージ関数がObject.prototypeを汚染

// 以後、すべてのオブジェクトが影響を受ける
const obj = {};
console.log(obj.polluted); // true — 定義していないのに値がある

典型的な原因は、ユーザー入力を再帰的にマージする処理で __proto__constructor.prototype を特別扱いしていないケース。
JSONパーサ、クエリストリングパーサ、ディープマージ系ライブラリが過去に繰り返しこの問題を出している。

過去にprototype pollutionが見つかった主要ライブラリ

ライブラリCVE / 時期影響
lodash(_.merge, _.defaultsDeepCVE-2018-16487, CVE-2019-10744ディープマージでの汚染
jQuery($.extendCVE-2019-11358ディープコピーでの汚染
Hoek(hapi)CVE-2018-3728ディープクローンでの汚染
minimistCVE-2020-7598コマンドライン引数パースでの汚染
qsCVE-2022-24999クエリストリング解析での汚染

これらは「汚染を起こす側」で、今回のaxiosは「汚染を受け取って悪用可能にしてしまう側」つまりgadgetだ。

攻撃チェーン

今回の脆弱性は「axios単体で完結する攻撃」ではない。
他のライブラリでprototype pollutionが起き、その汚染をaxiosが拾って実際の攻撃に変換する、という連鎖型の構造になっている。

graph TD
    A["外部入力<br/>JSON / クエリストリング等"] --> B["脆弱なライブラリ<br/>ディープマージ / パーサ"]
    B --> C["Object.prototype 汚染<br/>ヘッダ名・値が注入される"]
    C --> D["axiosの設定マージ<br/>汚染プロパティを拾う"]
    D --> E["HTTPリクエスト送信<br/>汚染値がヘッダに入る"]
    E --> F["CRLF注入成功<br/>ヘッダに改行文字混入"]
    F --> G1["リクエスト分割<br/>HTTP Request Smuggling"]
    F --> G2["SSRF<br/>内部API・メタデータ窃取"]
    F --> G3["認証ヘッダ上書き<br/>セッションハイジャック"]

ステップ2→3→4の流れがポイントで、axiosは自分でprototypeを汚染しない。
だが「汚染済みの世界」で動くと、設定マージが汚染値を正規の設定として扱い、そのままHTTPヘッダに載せてしまう。
ここでCRLFが入っていれば、HTTPプロトコル的にはヘッダの終端やリクエストの境界を偽装できる。

CRLFヘッダ注入の具体的な動き

HTTPヘッダは \r\n(CRLF)で区切られている。
ヘッダ値にCRLFを注入できると、攻撃者は任意のヘッダを追加したり、リクエスト本文を偽装できる。

正常なリクエスト:
GET /api/data HTTP/1.1
Host: example.com
X-Custom: normalvalue

CRLF注入後:
GET /api/data HTTP/1.1
Host: example.com
X-Custom: injected\r\nX-Forwarded-For: 127.0.0.1\r\n\r\nGET /admin HTTP/1.1\r\nHost: internal-api

2つ目の例では、X-Custom ヘッダの値にCRLFを入れることで X-Forwarded-For ヘッダの偽装と、2つ目のHTTPリクエストの注入(HTTP Request Smuggling=リクエスト分割攻撃)が起きている。
Node.jsのサーバサイドでは、この2つ目のリクエストが内部APIに向かうとSSRFになる。

axiosでの再現イメージ

// prototype が汚染された状態
Object.prototype['X-Injected'] =
  'value\r\nX-Forwarded-For: 169.254.169.254';

// axiosがリクエストを送信
const response = await axios.get('https://api.example.com/data');
// → X-Injected ヘッダが自動的に付与される
// → CRLFにより X-Forwarded-For も注入される

axiosの設定マージ(mergeConfig)は、デフォルト設定→インスタンス設定→リクエスト設定を順に重ねていく。
この過程で Object.prototype のプロパティが headers オブジェクトに紛れ込み、値がそのままHTTPヘッダとして送信されていた。

1.15.0の修正では、ヘッダ値に含まれる \r\n を検証し、存在すれば拒否するバリデーションが追加された。

CVSS 10.0の内訳

CVSS 10.0(あるいは9.9)というスコアは「すべての環境で即死級」という意味ではない。
NVDの評価は最悪ケースを想定しており、それはNode環境でのSSRF、特にAWS IMDSv2メタデータ取得やクラウド権限奪取まで連鎖するシナリオを含む。

環境リスク理由
Node.js サーバ(内部API呼び出しあり)SSRF→クラウドメタデータ取得→権限昇格の最悪チェーンが成立する
サーバレス / LambdaIAMロール付きの実行環境でメタデータエンドポイントへのSSRFが致命的
CI / ビルドパイプライン中〜高secretsやトークンが環境変数にある場合、SSRF経由で窃取される
ブラウザフロントエンドSame-origin policyがある。prototype pollution自体もブラウザでは起きにくい

「Node側で内部ネットワークやクラウドメタデータに到達できるか」が深刻度を分ける最大の要因だ。

刺さる条件チェックリスト

以下のうち2つ以上当てはまるなら優先対応。

  • axios < 1.15.0 を使っている
  • サーバ(Node.js)側で axios を使っている
  • 外部入力をもとに axios でリクエストを組み立てている(URL・ヘッダ・パラメータ)
  • 依存ツリーにディープマージ系・クエリパーサ系のライブラリが多い
  • 実行環境から内部ネットワークやクラウドメタデータへアクセス可能

確認コマンド。

# axiosのバージョン確認
npm ls axios
# または
pnpm ls axios

対処は axios@1.15.0 以上への更新。