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, _.defaultsDeep) | CVE-2018-16487, CVE-2019-10744 | ディープマージでの汚染 |
jQuery($.extend) | CVE-2019-11358 | ディープコピーでの汚染 |
| Hoek(hapi) | CVE-2018-3728 | ディープクローンでの汚染 |
| minimist | CVE-2020-7598 | コマンドライン引数パースでの汚染 |
| qs | CVE-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→クラウドメタデータ取得→権限昇格の最悪チェーンが成立する |
| サーバレス / Lambda | 高 | IAMロール付きの実行環境でメタデータエンドポイントへの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 以上への更新。