1月14日、Node.js Blogで「Node.js — Mitigating Denial-of-Service Vulnerability from Unrecoverable Stack Space Exhaustion for React, Next.js, and APM Users」と題した記事が公開された。この記事では、<監視ツールやモダンなフレームワークを使用している環境において、深いネスト構造を持つデータによってNode.jsがキャッチ不能なクラッシュを起こす脆弱性>について詳しく紹介されている。
以下に、その内容を簡潔にまとめて紹介する。
「効率的すぎるDoS攻撃」の正体
一般にDoS攻撃といえば、数万台のPCから一斉にアクセスを浴びせるような「物量作戦」をイメージする。しかし、今回の脆弱性は全く異なる。攻撃者に必要なのは、わずか一通の「極限までネストされたJSONデータ」だけだ。
[
[
[
[
/* ...5万階層続くブラケット... */
]
]
]
]
このデータをサーバーが処理しようとした瞬間、Node.jsはスタック領域(関数の呼び出し情報を溜めるメモリ)を使い果たす。通常ならエラーとして処理されるはずが、特定の条件下では「安全装置」が働かず、プロセスが即座に消滅(即死)する。 攻撃者からすれば、これほどコストパフォーマンスの良い攻撃はない。
なぜ「監視ツール」を入れるとより危険なのか
この脆弱性の最も皮肉な点は、「システムの安定稼働のために導入したツール」が、クラッシュの引き金になっていることだ。
- APM(監視ツール): Datadog、New Relic、Dynatrace、OpenTelemetryなど。
- モダンなフレームワーク: Next.jsやReact(Server Components)など。
これらはリクエストを追跡するために、Node.js内部の「非同期フック(async_hooks)」という仕組みを利用する。このフックが有効になっていると、Node.jsは内部処理を「絶対に失敗してはいけない聖域(fatal領域)」として実行する。
スタックオーバーフローが発生した際、運悪くこの「聖域」が実行中だと、Node.jsは「システムに致命的な異常が起きた」と判断し、try-catchなどのエラー処理をすべてバイパスして、自らプロセスを強制終了させてしまう。 本来、システムを守るための監視機能が、このバグによって「自爆スイッチ」へと変貌していたのである。
実際にサーバーが沈むシナリオ
例えば、ユーザーからのデータを再帰的に処理する以下のような一般的なコードが、格好の標的となる。
// Next.js APIルートなどの例
export default async function handler(req, res) {
try {
const data = req.body;
// 攻撃者が5万階層のデータを送ると、ここでスタック不足が発生
const result = processNestedData(data);
res.json({ success: true, result });
} catch (err) {
// 脆弱性が存在する場合、このキャッチブロックは完全に無視される。
// ログすら残せず、サーバーは突然死する。
console.error('Failed:', err);
}
}
function processNestedData(data) {
if (Array.isArray(data)) {
return data.map(item => processNestedData(item));
}
return transform(data);
}
監視ツールを入れていない環境なら RangeError として捕捉できていたものが、APMやNext.jsを導入した途端、「エラーにすらならない突然死」へと変わるのだ。
教訓:ランタイムの「親切」に依存してはいけない
Node.jsチームはこのバグを修正し、スタック不足が「聖域」で起きてもユーザーにエラーを返すように変更した。しかし、彼らはこうも警告している。「スタックオーバーフローの挙動は言語仕様で保証されているものではない」と。
- 即時のアップデート: 1月13日リリースのパッチ(v20.20.0, v22.22.0など)を適用すべきだ。
- 入力の制限: 外部から来るデータの「ネストの深さ」は必ずサニタイズし、制限を設けるべきだ。
「物量」ではなく「構造」で殺す。この巧妙なDoS攻撃からシステムを守るには、ランタイムの修正に頼るだけでなく、開発者がデータの「形」を疑うという基本に立ち返る必要がある。
詳細は[Node.js — Mitigating Denial-of-Service Vulnerability from Unrecoverable Stack Space Exhaustion for React, Next.js, and APM Users」を参照していただきたい。