Node.js脆弱性(CVE-2026-111497):手元のMacのNode.jsは、修正版が出ない25.xだった【Homebrew】

Node.js脆弱性CVE-2026-111497と、Homebrewで25.xから26.12.1へ上げた記録のアイキャッチ Web Development
この記事は約13分で読めます。

手元のMacで、いま node -v を打ったら、何と返ってくるでしょうか。
私が10月22日の夕方にこれを打ったとき、返ってきたのは v25.8.1。前の日にNode.jsが10件の脆弱性を直すセキュリティリリースを出していて、修正版は22・24・26の3系統だけ。25という数字は、そのどこにもありませんでした。

この記事は、そこから26.12.1に上げるまでの記録です。手順そのものは brew upgrade node の一行で終わる話なのですが、出てきたログを読んでいくと、Homebrewで入れたNode.jsならではの事情がいくつか見えてきました。同じようにHomebrewでNode.jsを入れている方には、たぶん他人事ではないと思います。

Node.jsのリリース系列の図。偶数版の22・24・26は長期サポートで今回の修正対象、奇数版の25はサポート終了済みで修正版が出ない
25は、修正版が出る3系統のどれにも入っていません。
HomebrewのNode.jsが、brotli・c-ares・openssl@4・ca-certificatesなどを別パッケージとして共有し、brew upgrade nodeでまとめて更新される様子の図

10月21日:PHPの次は、Node.jsだった

前の晩は、PHPの「php2shell」の修正版を待っていました。その話は別の記事に書いたので省きますが、同じ10月21日(現地時間)に、Node.jsもセキュリティリリースを出していました。予告は2日前の19日に出ていたので、PHPと同じ週に、二つの実行環境の修正が重なったことになります。

直された脆弱性は10件で、深刻度の内訳はHighが4件、Mediumが3件、Lowが3件です。CVSSの数値は公表されていません。いちばん重いのは、Node.jsが同梱しているBrotli(圧縮形式の一つ)の展開処理の不具合で、細工した圧縮データを展開させると、メモリの範囲外に書き込みが起き、リモートからコードを実行される可能性がある、とされています。

深刻度 CVE 中身 影響する系列
High CVE-2026-111497 Brotliの展開処理で範囲外書き込み(リモートからのコード実行の可能性) 22 / 24 / 26
High CVE-2026-106069 正規の書き方でないIPアドレスで、TLSのホスト名検証を回避できる 22 / 24 / 26
High CVE-2026-105483 全角数字で書いたIPアドレスで、TLSのホスト名検証を回避できる 24 / 26
High CVE-2026-105482 dns.resolve が、名前の途中のnullバイトで名前を切り詰める 22 / 24 / 26
Medium CVE-2026-105481 keep-aliveの接続で、リクエスト前に書かれた応答を処理してしまう 22 / 24 / 26
Medium CVE-2026-106072 正規形でないIPv4の扱いで、DNS名の証明書がIP宛ての接続を認証できてしまう 22 / 24 / 26
Medium CVE-2026-106074 スナップショットや単一実行ファイル(SEA)で、randomUUID() などが毎回同じ値を返す 22 / 24 / 26
Low CVE-2026-106070 ZIPのヘッダー検証に失敗しても、攻撃者が仕込んだバイトを返す 26
Low CVE-2026-106071 Permission Modelのワイルドカード検索で、ファイルのアクセスが広く許されすぎる 26
Low CVE-2026-106073 fs.realpathSync で、Permission Modelを回避できる 22 / 24 / 26

Brotliの件は、22日の時点で実際の攻撃は報告されていません。公開された攻撃コードも、わざと守りを弱くしたテスト用のビルドでしかコード実行に至らず、通常のビルドではクラッシュとメモリの一部の読み取りにとどまった、と報じられています。攻撃の通り道は、node:zlib で直接展開する場合と、fetch() がBrotli圧縮の応答を自動で展開する場合の2つです。外から受け取った圧縮データを展開するサーバーが、いちばん急ぐべき立場になります。
PHPのときは、ほぼすべてのPHPサイトが既定の設定のまま狙われうる、という話でした。それに比べると、今回は条件がつく分、少し落ち着いて構えられる話です。ただ、修正版の出ない系列を使っている場合は、話が別になります。

同じリリースでは、ほかにc-ares、OpenSSL、SQLite、undiciといった依存ライブラリの更新と、秘密鍵の漏えいが報告されたインドの認証局eMudhraの中間証明書を拒否する変更も入っています。

10月22日 夕方:node -v が返したのは v25.8.1

私はWordPressのプラグインを作っていて、本番のサイトはPHPで動いています。Node.jsは、手元のMacでビルドツールを動かしたり、Claude Codeから呼ぶMCPサーバーを起動したりするのに使っています。外からのデータを受け取るサーバーとしてNode.jsを動かしているわけではないので、確かめるのは手元のNode.jsの版だけです。そこで node -v を打ったのが、冒頭の場面です。

Node.jsは、偶数の版が長期サポート(LTS)になり、奇数の版は半年ほどで役目を終える、という周期で出ています。25.xは奇数の版で、すでにサポートが終わっていました。サポートが終わった系列には、セキュリティの修正は出ません。今回の修正版が22・24・26の3系統だけだったのは、そういう理由です。
私のMacが25.xだった理由には、心当たりが一つしかありません。Homebrewで brew install node と入れていたからです。Homebrewの node は、その時点でいちばん新しい版を指します。入れた、あるいは最後に上げた時点では25.xがいちばん新しかったので、それが入ったのだと思います。そのあと25.xのサポートが終わっても、私が何もしなければ、Macの中ではずっと25.xのままです。最新を入れたつもりが、気づかないうちにサポートの切れた版に乗っていた、ということになります。

余談ですが、このとき最初に打ったのは、Node.jsのバージョン管理ツールの n でした。返ってきたのは command not found。夏に古い開発環境を片づけたとき、使っていなかったnもまとめて外していたのを、すっかり忘れていました。手が覚えているコマンドというのは、なかなか抜けないものです。

同じ夕方:brew upgrade node を実行する

やったことは、これだけです。一度 brew --upgrade node と打ち間違えて叱られ、打ち直しています。

実行したのは、MacBook Pro(Apple Silicon)、macOS 27 の環境です。以下のログは、実際の出力から、ダウンロードの進捗表示などを省いて抜き出したものです。

結果だけ見れば、25.8.1から26.12.1へ、メジャーバージョンを一つまたいで上がりました。26.12.1は、今回のセキュリティリリースの修正版そのものです。これで手元のNode.js本体は、修正済みになりました。
ただ、このログには、ほかにも読むべきところがありました。以下、気になった箇所を一つずつ見ていきます。

ログを読む:Brotliは、別のパッケージとして上がっていた

依存パッケージの一覧に、brotli 1.2.0 -> 1.2.1 という行があります。最初は読み流しそうになったのですが、これは今回の本命の脆弱性に直結する行でした。

nodejs.org から落とす公式のNode.jsは、Brotliを本体の中に同梱しています。だから公式の配布物では、Node.js本体を上げれば、中のBrotliも一緒に直ります。一方、HomebrewのNode.jsは、Brotliを本体に抱えず、Homebrewの別のパッケージとして共有しています。今回の修正は、Brotliの上流の修正を取り込んだもので、同じ修正はBrotli 1.2.1にも入っている、と報じられていました。HomebrewのNode.jsの場合、Brotliの修正はNode.js本体とは別に、brotliパッケージが1.2.1に上がることで届いていた、ということです。
ほかの行も、同じ目で見ると意味がつながります。c-aresは1.34.9で、今回のNode.jsのリリースで上げられた版と同じです。ca-certificatesの日付は2026-10-20で、eMudhraの証明書の件が報告された直後の更新です。Node.jsのセキュリティリリースの中身が、Homebrewでは何本ものパッケージに分かれて届いている様子が、このログからよく見えます。

パッケージ 更新前 更新後 今回のリリースとの関係
node 25.8.1_1 26.12.1 本体。26系の修正版
brotli 1.2.0 1.2.1 CVE-2026-111497の修正を含む上流の版
c-ares 1.34.6 1.34.9 Node.jsのリリースで上げられた版と同じ
ca-certificates 2026-07-16 2026-10-20 eMudhraの件のあとの証明書一覧
openssl@4 (なし) 4.0.3 新しく入った。openssl@3のリンクが外れる

逆に言うと、HomebrewでBrotliを使っているほかのツールも、この brew upgrade で一緒に修正版のBrotliを使うようになっています。Brotliを同梱しているほかのソフトにも更新が必要になりうる、という注意が報道にありましたが、Homebrewの範囲については、Node.jsを上げたついでに片づいていたことになります。

ログを読む:openssl@3 のリンクが外れていた

もう一つ、目が止まった行があります。

Unlinking /opt/homebrew/Cellar/openssl@3/3.6.3… 6566 symlinks removed.
26.12.1のNode.jsは openssl@4 を使うため、openssl@4 が新しく入り、それまで /opt/homebrew の下に張られていた openssl@3 のリンクが外されています。

Homebrewのほかのパッケージは、自分が使うOpenSSLの版を直接参照しているので、ふつうはこれで動かなくなることはないはずです。変わるのは、ターミナルで openssl コマンドを打ったときに動く版や、自分で何かをビルドするときに拾われるヘッダーです。openssl version を打てば、いまどちらが使われているかが分かります。OpenSSLを直接使う作業をしている方は、一度確かめておくと安心です。

ログを読む:古い25.8.1は、もう残っていない

最後の Cleanup では、Removing: /opt/homebrew/Cellar/node/25.8.1_1 と、古い版がまるごと削除されています。Homebrewの既定の動きで、ディスクは空きますが、25.8.1に戻すことはもうできません。
今回のように、メジャーバージョンを一つまたいで上がった場合、手元のプロジェクトが新しい版で動かないことも、ないとは言えません。あとから戻れないのは、少し怖いところです。ビルドの多いプロジェクトを抱えている方は、上げたあとにいつものビルドを一度通しておくと安心です。

振り返ると、そもそも node という「いちばん新しい版」を指すパッケージを入れていたことが、今回の出発点でした。Homebrewには、系列を固定した node@24 のようなパッケージもあります。偶数の長期サポート版に固定しておけば、サポートの切れた奇数版に知らないうちに乗っている、ということは起こりにくくなります。そのかわり、新しい系列に移るときは自分で切り替える必要があります。どちらが合うかは、Node.jsをどれだけ使うかによると思います。

ちなみに、ログの最後の Single Executable Application is disabled という一文は、HomebrewのNode.jsでは単一実行ファイル(SEA)を作れない、という意味です。今回のMediumの1件(SEAやスナップショットで randomUUID() が同じ値を返す)は、そもそもHomebrewのNode.jsでSEAを作っている人には当てはまらない、ということになります。

そのあと:確かめたこと

確認すること コマンド・方法 見るところ
Node.jsの版 node -v 26.12.1/24.22.1/22.23.4 以上になっているか
古いNode.jsが残っていないか which -a node 先頭に出るのが /opt/homebrew/bin/node か。別の場所の古いNode.jsが先に見つかると、そちらが使われる
OpenSSLの版 openssl version openssl@4 に切り替わっていても困らないか
動いているプロセス Claude Code などを開き直す npx で起動したMCPサーバーなどは、起動したときのNode.jsのまま動き続けている

最後の行は、見落としやすいと思います。Node.jsを上げても、すでに動いているプロセスは古い版のままです。Claude CodeからnpxでMCPサーバーを起動している場合、セッションを開き直すまでは、更新前のNode.jsで動いています。

本番のサイトはPHP、手元の開発はNode.js。同じ週に両方の脆弱性が出て、サーバーのほうはホスティング会社が一晩で直してくれましたが、手元のMacは、自分で node -v を打たなければ誰も教えてくれませんでした。
もし、まだ打っていなければ、いまターミナルを開いて node -v を打ってみてください。返ってきた数字が奇数だったら、それが今日やることです。

参考にした情報

Rapls Works のその他の記事

Web Development
この記事を書いた人
rapls

WordPress のプラグインを作っているフリーランスエンジニア、Rapls です。Web 開発は6年以上。WordPress.org でプラグインを6本公開・保守しながら、日本語ロケールの翻訳エディター(PTE)も務めています。このブログには、現場で実際にハマって、調べて、直した話を書いています。

raplsをフォローする
raplsをフォローする

コメント

タイトルとURLをコピーしました