Node.jsの2026年10月のセキュリティ更新|24.22.1などで変わったTLS・DNS・権限チェックの挙動を、修正前の版と比べて検証
Node.jsは10月21日、3つの系列にセキュリティ更新を出した
サーバーやコマンドラインツールでJavaScriptを動かすNode.jsのプロジェクトは2026年10月21日、サポート中の3つの系列にセキュリティ更新を公開しました。修正版は22.23.4、24.22.1(長期サポート版の最新)、26.12.1(最新版)で、重大度Highの4件を含む10件の欠陥を直しています。
直った箇所は、TLSで接続先の証明書を確かめる処理、DNSの名前解決、Brotli形式の解凍、ファイルの読み取りを制限する権限モデルなど、多くのアプリが使う機能にわたります。修正の中には、これまで通っていた入力をエラーにする変更も含まれます。
この記事では、修正前の24.22.0と修正後の24.22.1を手元のMacで並べて動かし、挙動が変わった6件を実際の出力で示します。そのうえで、自分の環境をどの版へ上げればよいか、更新後に点検したいコードの書き方を整理します。
更新の要点
22.23.4・24.22.1・26.12.1へ上げると、10件の欠陥がまとめて直る
修正の数
10件(High 4件)
Highは、DNSの名前の切り詰め、TLSのIPアドレスの照合2件、Brotliの解凍。
上げる先
22.23.4 / 24.22.1 / 26.12.1
使っている系列の、この版以降へ。サポートが終わった系列は修正の対象外。
手元で確かめた違い
6件
24.22.0で通っていた入力が24.22.1では拒否やエラーになり、スナップショットの乱数は起動ごとに変わるようになった。
出典: Node.js「October 2026 Security Releases」(2026年10月21日)。手元の検証はmacOS 27.0.1、2026年10月24日。
使っている系列の修正版へ上げれば、10件すべてに対応できる
最初に、自分の環境で動いているNode.jsの版を確かめます。ターミナルで次を実行すると、版が表示されます。
node -v
表示された版の先頭の数字(系列)ごとに、上げる先は次のとおりです。修正の一部は特定の系列にだけ関係します。
22.x(長期サポート版)
22.23.4
対象7件。このほか、OpenSSLとSQLiteも更新される。
24.x(長期サポート版の最新)
24.22.1
対象8件。全角数字のIPアドレスの照合は24.xと26.xだけの修正(22.xは対象外)。
26.x(最新版)
26.12.1
対象10件。ZIPの読み取りと、権限のワイルドカード指定の2件は26.xだけ。
Node.jsのプロジェクトは、サポートが終わった版を、セキュリティ更新のたびに「影響を受ける版」として扱っています。20.x以前を使っている場合は、修正版が出ないため、サポート中の系列へ移るのが前提になります。
TLSの証明書の照合は、IPアドレスの変わった書き方を受け付けなくなった
High 4件のうち2件と、Medium 1件は、TLSで接続するときに「接続先の名前と、証明書に書かれた名前が一致するか」を確かめる処理(tls.checkServerIdentity())の欠陥です。証明書には、その証明書を使ってよい名前やIPアドレスの一覧(SAN)が書かれています。IPアドレスで接続したときは「IP Address:」の項目だけと照合し、「DNS:」の項目に書かれた数字は使わないのが正しい動きです。
IPアドレスの127.0.0.1は、127.1、0x7f.0.0.1、2130706433のように書くこともでき、OSによってはどれも同じ127.0.0.1として接続します。修正前のNode.jsは、こうした書き方や全角数字の「127.0.0.1」で接続したとき、証明書に「DNS:127.0.0.1」という名前の形で書かれていれば受け入れていました。正しい書き方の127.0.0.1では拒否される証明書なのに、変わった書き方では通っていたことになります。
手元で、証明書の名前一覧をいくつか用意し、照合の結果を新旧の版で比べました。表の1行目が全角数字の件(High)、2行目が変わった書き方の件(Medium)、3行目が先頭にゼロを付けた件(High)に当たります。3行目の仕組みは表のあとで説明します。
| 接続先の書き方 | 証明書の名前 | 24.22.0 | 24.22.1 |
|---|---|---|---|
| 127.0.0.1(全角数字) | DNS:127.0.0.1 | 受理 | 拒否 |
| 127.1 / 0x7f.0.0.1 / 2130706433 | DNS:127.0.0.1 | 受理 | 拒否 |
| 0127.0.0.1(先頭にゼロ) | DNS:87.0.0.1 | 受理 | 拒否 |
| 127.0.0.1(正しい書き方) | DNS:127.0.0.1 | 拒否 | 拒否 |
| 127.0.0.1(正しい書き方) | IP Address:127.0.0.1 | 受理 | 受理 |
tls.checkServerIdentity() の戻り値で判定(3行目は「DNS:」の形の証明書で確認)。macOS 27.0.1、nodejs.orgの公式バイナリで2026年10月24日に確認。色付きのセルが、修正で結果が変わった箇所。
先頭にゼロを付けた0127.0.0.1は、ブラウザと同じURLの規則では8進数として87.0.0.1と読まれます。一方、macOSの名前解決は同じ文字列を127.0.0.1として扱いました。手元の結果からは、修正前の照合が0127.0.0.1を87.0.0.1に読み替えたうえで、IPアドレスとしてではなく名前として、証明書の「DNS:」の項目と比べていたことが分かります。そのため「DNS:87.0.0.1」の証明書が通り、「IP Address:87.0.0.1」の証明書は修正前も拒否されました。実際の接続先は127.0.0.1なので、照合と接続先がずれていたことになります。
v24.22.0 new URL host -> 87.0.0.1
v24.22.0 dns.lookup(0127.0.0.1) -> 127.0.0.1
照合の関数だけでなく、実際の接続でも違いが出るかを確かめるため、「DNS:127.0.0.1」だけを持つ自己署名証明書でローカルのTLSサーバーを立て、接続先の書き方を変えてつなぎました。
node v24.22.0
127.0.0.1: ERR_TLS_CERT_ALTNAME_INVALID
127.1: 接続成功(証明書を受理)
0x7f.0.0.1: 接続成功(証明書を受理)
node v24.22.1
127.0.0.1: ERR_TLS_CERT_ALTNAME_INVALID
127.1: ERR_TLS_CERT_ALTNAME_INVALID
0x7f.0.0.1: ERR_TLS_CERT_ALTNAME_INVALID
24.22.1では、変わった書き方でも正しい書き方と同じ結果になりました。IPアドレスで接続する仕組みを使っているなら、証明書には「IP Address:」の形でIPアドレスを書き、接続先も正しい書き方の127.0.0.1の形で指定すると、新旧どちらの版でも同じ判定になります。
DNSの名前にNUL文字が入っていると、エラーで止まるようになった
Highの3件目は、dns.resolve4()などの名前解決の関数とdns.reverse()が、名前の途中にNUL文字(\0)があると、そこで名前を切り詰めて問い合わせていた欠陥です。たとえば「attacker.example\0.trusted.example」を渡すと、末尾が「trusted.example」なので許可された名前に見えても、実際にはattacker.exampleを問い合わせます。
手元で、127.0.0.1にDNSサーバーを立てて、届いた問い合わせの名前を記録しました。外部のDNSには問い合わせていません。
node v24.22.0
resolve4("attacker.example\u0000.trusted.example")
DNSサーバーに届いた問い合わせ: attacker.example
結果: ENOTFOUND
node v24.22.1
resolve4("attacker.example\u0000.trusted.example")
結果: ERR_INVALID_ARG_VALUE
24.22.1では、DNSサーバーに問い合わせる前にERR_INVALID_ARG_VALUEのエラーになり、問い合わせ自体が出ていません。同じ種類の問題は以前にdns.lookup()とnet・tlsの接続先の指定についてだけ直されていて、今回はその直し漏れを埋めた修正だとNode.jsは説明しています。
権限モデルの抜け道と、スナップショットの乱数の重複も直った
残りの検証は、重大度がMediumとLowの2件です。どちらも特定の機能を使うアプリだけに関係しますが、使っている場合は挙動の違いがはっきり出ます。
権限モデル(Low)
realpathSyncが読み取り制限に従うようになった
--permissionで読めるフォルダを絞っても、fs.realpathSync()だけは許可の外にあるファイルの存在や実際の場所を調べられた。
スナップショット(Medium)
起動のたびに同じUUIDが出なくなった
Node.jsは乱数をまとめて作り置きしている。起動を速くするための保存ファイル(スナップショット)を作るときに乱数の関数を呼ぶと、その作り置きごと保存され、起動するたびに同じ値が出ていた。
権限モデルでは、読み取りをallowedフォルダだけに許可し、外側のoutside/secret.txtに対して2つの関数を呼びました。修正前はfs.realpathSync()だけが許可の外のファイルの実際の場所を返しています。
node --permission --allow-fs-read=./allowed ... realpath-perm.mjs ./outside/secret.txt
node v24.22.0
fs.realpathSync: 成功 -> …/sample/outside/secret.txt
fs.realpathSync.native: ERR_ACCESS_DENIED
node v24.22.1
fs.realpathSync: ERR_ACCESS_DENIED
fs.realpathSync.native: ERR_ACCESS_DENIED
スナップショットでは、作成時にcrypto.randomUUID()を1回呼んでおき、そのスナップショットから3回起動して、最初に作られるUUIDを表示しました。
node v24.22.0(3回起動)
eb6a4ba2-abff-49c3-9ee7-6abf47dd5bca
eb6a4ba2-abff-49c3-9ee7-6abf47dd5bca
eb6a4ba2-abff-49c3-9ee7-6abf47dd5bca
node v24.22.1(3回起動)
928977c9-115f-4159-b481-e05a81f95116
cc7f6986-f696-47f3-b349-63b7b6d17c78
e76bd7e7-d33a-4953-8e44-03e683226f76
修正前は3回とも同じUUIDでした。Node.jsのセキュリティ告知(アドバイザリ)によると、起動直後に作られる最大127個のUUIDが、起動のたびに同じ並びで出ます。crypto.randomInt()でも、最初の約1,000個の値が起動ごとに同じになります。スナップショットや、スナップショットを使う単一実行ファイル(SEA)でアプリを配っている場合、IDの衝突や、別のプロセスの値の予測につながるおそれがあります。
Brotliの解凍の修正は、信頼できないデータを扱うアプリほど優先度が高い
残るHighの1件は、node:zlibでBrotli形式のデータを解凍する処理の範囲外書き込みです。壊れたデータを「入力の続きを待つ状態」と取り違え、8バイトの領域へ入力を書き足し続けていました。外部から受け取ったデータを解凍するアプリでは、プロセスの停止、メモリの内容の漏えい、遠隔からのコード実行につながるとNode.jsは説明しています。
この欠陥は攻撃用のデータを作らないと再現できないため、手元では動かしていません。HTTPの圧縮されたレスポンスやアップロードされたファイルなど、自分で作っていないデータを解凍しているかどうかが、更新を急ぐかの目安になります。
Medium・HTTPの使い回し
接続を使い回すHTTPクライアントで、頼む前に届いていた応答を、次のリクエストへの返事として読んでしまうことがあった。
Low・26.xだけ
zlib.ZipFileが、ヘッダーの確認に失敗したあとに別の場所の中身を返すことがあった。--allow-fs-read=/etc/passwd.*のようなワイルドカード指定が、短い/etc/passwdまで許可していた。
同梱ライブラリの更新
DNSのc-ares、HTTPクライアントのUndici、同梱のCA証明書(eMudhraの中間CAの侵害への対応)は全系列。OpenSSLは24.xと22.x、SQLiteは22.x。
更新したら点検したい4つの書き方
今回の修正のうち、TLS(3件)、DNS、権限モデルの修正は、これまで通っていた入力をエラーにします。手元で確かめた挙動から、更新後に点検しておきたい書き方をまとめます。
1. IPアドレスで接続するTLS
接続先を
127.1や0x7f.0.0.1、全角数字などの変わった書き方で指定し、「DNS:」の形で数字を書いた証明書で通していた接続は、更新後にERR_TLS_CERT_ALTNAME_INVALIDで止まる。接続先は127.0.0.1の形で書き、証明書には「IP Address:」でIPアドレスを入れる。2. 外から受け取った名前でのDNS問い合わせ
利用者が入力したホスト名を
dns.resolve*()に渡しているなら、ERR_INVALID_ARG_VALUEを受け止める処理を用意する。名前の確認は、問い合わせに使う文字列そのもので行う。3. 権限モデルで動かしているスクリプト
fs.realpathSync()が許可の外のパスでERR_ACCESS_DENIEDになる。必要なフォルダを--allow-fs-readに足す。4. スナップショットやSEAで配っているアプリ
作成中に
randomUUID()などの乱数の関数を呼んでいたなら、修正版のNode.jsでスナップショットを作り直す。修正前の版で作った保存ファイルには、作り置きの乱数が残っている。
今回の検証で印象的だったのは、TLSの照合のように、正しい書き方では拒否される証明書が、変わった書き方だけで通っていた点です。更新そのものは系列の最新版へ上げるだけで済みます。そのあとでIPアドレスの書き方とエラー処理を見直しておけば、更新後に接続や処理が思わぬところで止まる事態を避けやすくなります。












