Node.js HTTP/2サーバーが1リクエストでダウン:CVE-2025-59465の攻撃手法と防御策
Node.js の HTTP/2 サーバーに、不正なリクエスト1件でプロセス全体がクラッシュする深刻な脆弱性(CVE-2025-59465)が発見されました。本記事では、この脆弱性の攻撃手法・影響範囲・防御策を、インフラエンジニア・SRE の視点から徹底解説します。
「HTTP/2 の脆弱性対策」「Node.js サーバーの DoS 防御」「TLS エラーハンドリング」を知りたい方に向けた記事です。
HTTP/2 脆弱性の影響比較表
| # | 構成 | HTTP/2 使用 | エラーハンドラー | 攻撃時の動作 | 緊急度 |
|---|---|---|---|---|---|
| 1 | Node.js 直接 + HTTP/2 | あり | なし | 即座にクラッシュ | 最優先 |
| 2 | Node.js 直接 + HTTP/2 | あり | あり | 接続切断のみ | 低(対策済み) |
| 3 | Nginx → Node.js(HTTP/1.1) | Nginx のみ | - | 影響なし | 低 |
| 4 | ALB → Node.js(HTTP/1.1) | ALB のみ | - | 影響なし | 低 |
| 5 | Fastify + HTTP/2 | あり | フレームワーク依存 | 要確認 | 高 |
| 6 | Express + spdy | あり | なし | 即座にクラッシュ | 最優先 |
この表は即答用です。各構成での詳細な影響と対策は後段で解説します。
検証環境
- OS: macOS Sonoma 14.7 / Ubuntu 22.04 LTS
- Node.js: 22.21.0(脆弱性確認)→ 22.22.0(修正確認)
- 主要パッケージ:
- fastify: 5.2.0
- @fastify/http2: 1.0.0
- spdy: 4.0.2
- nghttp2: 1.64.0(攻撃検証用)
- 検証日: 2026年1月15日
背景:HTTP/2 と Node.js の関係
この章でわかること
- HTTP/2 の基本(初学者向け)
- Node.js での HTTP/2 実装方法
- 脆弱性が発生する技術的背景
HTTP/2 の基本(初学者向け)
HTTP/2 は、HTTP/1.1 の後継プロトコルで、以下の特徴があります。
- 多重化: 1つの TCP 接続で複数のリクエストを同時処理
- ヘッダー圧縮: HPACK アルゴリズムによる効率的な圧縮
- サーバープッシュ: クライアントのリクエスト前にリソースを送信
- バイナリフレーミング: テキストベースからバイナリ形式へ
mermaidflowchart LR
subgraph http1["HTTP/1.1"]
A1["リクエスト1"] --> A2["レスポンス1"]
A2 --> A3["リクエスト2"]
A3 --> A4["レスポンス2"]
end
subgraph http2["HTTP/2"]
B1["リクエスト1"] --> B3["レスポンス1"]
B2["リクエスト2"] --> B4["レスポンス2"]
B1 -.-> B2
B3 -.-> B4
end
図の補足:HTTP/1.1 では順次処理ですが、HTTP/2 では1つの接続で複数のリクエスト・レスポンスを同時に処理できます。
Node.js での HTTP/2 実装
Node.js では、組み込みの http2 モジュールを使用して HTTP/2 サーバーを構築できます。
javascriptconst http2 = require("http2");
const fs = require("fs");
// HTTP/2 は TLS が必須(h2 プロトコル)
const server = http2.createSecureServer({
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
});
server.on("stream", (stream, headers) => {
stream.respond({
"content-type": "text/html",
":status": 200,
});
stream.end("<h1>Hello HTTP/2</h1>");
});
server.listen(443);
HPACK とヘッダー圧縮
HTTP/2 では、ヘッダーを効率的に圧縮するために HPACK というアルゴリズムを使用します。HPACK は以下の仕組みで動作します。
- 静的テーブル: よく使われるヘッダー(
:method、:pathなど)を事前定義 - 動的テーブル: 通信中に使用されたヘッダーをキャッシュ
- ハフマン符号化: 文字列を圧縮
mermaidflowchart TD
subgraph hpack["HPACK エンコード"]
input["ヘッダー<br/>:method: GET<br/>:path: /api"]
static["静的テーブル参照"]
dynamic["動的テーブル参照"]
huffman["ハフマン符号化"]
output["圧縮されたバイナリ"]
end
input --> static
static --> dynamic
dynamic --> huffman
huffman --> output
図の補足:HPACK は複数の圧縮技術を組み合わせて、ヘッダーサイズを大幅に削減します。
脆弱性が発生する背景
今回の脆弱性は、不正な HPACK データを含む HEADERS フレームを受信した際に発生します。Node.js の HTTP/2 実装は、デコードエラーを適切に処理できず、TLSSocket で ECONNRESET エラーが発生してプロセス全体がクラッシュします。
つまずきポイント
- HTTP/2 は TLS が事実上必須(h2 プロトコル)であり、TLS 関連のエラーハンドリングが重要です
- 「Nginx の後ろにいるから大丈夫」と思っても、Nginx → Node.js 間で HTTP/2 を使用している場合は影響を受けます
課題:CVE-2025-59465 の詳細と攻撃手法
この章でわかること
- 脆弱性の技術的な詳細
- 攻撃の具体的な手法
- 影響範囲の確認方法
脆弱性の技術的詳細
| 項目 | 内容 |
|---|---|
| CVE番号 | CVE-2025-59465 |
| 深刻度 | HIGH |
| 影響バージョン | Node.js 20.x〜25.x(修正前) |
| 修正バージョン | 20.20.0, 22.22.0, 24.13.0, 25.3.0 |
| 攻撃種別 | リモート DoS(サービス拒否) |
| 認証要否 | 不要 |
| 攻撃の複雑さ | 低(1リクエストで攻撃可能) |
攻撃のメカニズム
攻撃は以下の流れで成立します。
mermaidsequenceDiagram
participant Attacker as 攻撃者
participant TLS as TLS レイヤー
participant HTTP2 as HTTP/2 レイヤー
participant Node as Node.js プロセス
Attacker->>TLS: TLS ハンドシェイク
TLS-->>Attacker: 接続確立
Attacker->>HTTP2: 不正な HEADERS フレーム<br/>(オーバーサイズの HPACK)
HTTP2->>HTTP2: HPACK デコードエラー
HTTP2->>TLS: 接続リセット試行
TLS->>Node: ECONNRESET エラー
Note over Node: エラーハンドラーなし
Node->>Node: uncaughtException
Node->>Node: プロセス終了
図の補足:攻撃者は TLS 接続を確立した後、不正な HTTP/2 フレームを送信するだけでサーバーをクラッシュさせられます。
攻撃ペイロードの構造
不正な HEADERS フレームは、以下の特徴を持ちます。
- オーバーサイズの HPACK データ: 動的テーブルのサイズ制限を超えるデータ
- 無効なハフマン符号: デコード不能な文字列
- 不正なインデックス参照: 存在しないテーブルエントリへの参照
scss# 不正な HEADERS フレームの構造(概念的)
+-----------------------------------------------+
| Frame Header (9 bytes) |
+-----------------------------------------------+
| Length (3) | Type (1) | Flags (1) | Stream ID |
| 0x1000 | 0x01 | 0x04 | 0x00000001|
+-----------------------------------------------+
| HPACK Encoded Headers |
| (オーバーサイズ / 無効なエンコーディング) |
+-----------------------------------------------+
攻撃の実証(検証環境のみ)
以下は、nghttp2 ライブラリを使用した攻撃検証の例です。本番環境では絶対に実行しないでください。
bash# nghttp2 のインストール(macOS)
brew install nghttp2
# 脆弱性の検証(ローカル環境のみ)
# 大きな HPACK データを含むリクエストを送信
nghttp -v -H ':method: GET' \
-H ':path: /AAAAA...(数千文字)' \
https://localhost:443/
# サーバーがクラッシュした場合、脆弱性あり
脆弱なコードパターン
パターン1: エラーハンドラーなし(最も危険)
javascript// 脆弱なコード
const http2 = require("http2");
const fs = require("fs");
const server = http2.createSecureServer({
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
});
server.on("stream", (stream, headers) => {
stream.respond({ ":status": 200 });
stream.end("OK");
});
// secureConnection イベントでエラーハンドラーがない
server.listen(443);
パターン2: session エラーのみハンドリング(不十分)
javascript// 不十分なコード
server.on("session", (session) => {
session.on("error", (err) => {
console.error("Session error:", err);
});
});
// TLSSocket のエラーはハンドリングされていない
パターン3: Fastify での HTTP/2(要確認)
javascript// Fastify + HTTP/2
const fastify = require("fastify")({
http2: true,
https: {
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
},
});
// Fastify のバージョンによってはエラーハンドリングが不十分
fastify.listen({ port: 443 });
影響範囲の確認方法
自身の環境が脆弱かどうかを確認するスクリプトです。
javascript// scripts/check-http2-vulnerability.js
const http2 = require("http2");
const fs = require("fs");
// テスト用の自己署名証明書を使用
const server = http2.createSecureServer({
key: fs.readFileSync("test-server.key"),
cert: fs.readFileSync("test-server.crt"),
});
let hasErrorHandler = false;
server.on("secureConnection", (socket) => {
// エラーハンドラーの有無をチェック
const listeners = socket.listeners("error");
if (listeners.length > 0) {
hasErrorHandler = true;
}
});
server.on("stream", (stream) => {
stream.respond({ ":status": 200 });
stream.end("OK");
});
server.listen(0, () => {
const port = server.address().port;
console.log(`Test server listening on port ${port}`);
// 自己接続してチェック
const client = http2.connect(`https://localhost:${port}`, {
rejectUnauthorized: false,
});
client.on("connect", () => {
setTimeout(() => {
client.close();
server.close();
if (!hasErrorHandler) {
console.log("⚠️ TLSSocket にエラーハンドラーがありません");
console.log(" CVE-2025-59465 の影響を受ける可能性があります");
} else {
console.log("✅ エラーハンドラーが設定されています");
}
}, 100);
});
});
つまずきポイント
- HTTP/2 の脆弱性は TLS レイヤーで発生するため、HTTP/2 のストリームイベントだけをハンドリングしても防げません
server.on('error')だけでは不十分で、secureConnectionイベントで TLSSocket のエラーをハンドリングする必要があります
解決策と判断:防御策の実装
この章でわかること
- 推奨される対策の優先順位
- 具体的なコード修正方法
- インフラレベルでの防御策
対策の優先順位
| 優先度 | 対策 | 効果 | 実装コスト |
|---|---|---|---|
| 1(最優先) | Node.js アップデート | 根本解決 | 低〜中 |
| 2 | エラーハンドラー追加 | 攻撃緩和 | 低 |
| 3 | リバースプロキシで HTTP/2 終端 | 攻撃回避 | 中 |
| 4 | WAF でのペイロード検査 | 攻撃検知・ブロック | 中〜高 |
Node.js アップデート(根本解決)
修正済みバージョンにアップデートすることで、根本的に解決できます。
bash# 修正済みバージョン
# - Node.js 20.20.0
# - Node.js 22.22.0
# - Node.js 24.13.0
# - Node.js 25.3.0
# nvm を使用している場合
nvm install 22.22.0
nvm use 22.22.0
# Docker の場合
FROM node:22.22.0-alpine
エラーハンドラーの追加(暫定対策)
Node.js をすぐにアップデートできない場合の暫定対策です。
javascript// 修正済みコード
const http2 = require("http2");
const fs = require("fs");
const server = http2.createSecureServer({
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
});
// TLSSocket のエラーハンドラーを追加(重要)
server.on("secureConnection", (socket) => {
socket.on("error", (err) => {
// エラーをログに記録し、接続を安全に閉じる
console.error("TLS Socket error:", {
message: err.message,
code: err.code,
remoteAddress: socket.remoteAddress,
});
// 接続は自動的に閉じられる
});
});
// HTTP/2 セッションのエラーハンドラーも追加
server.on("session", (session) => {
session.on("error", (err) => {
console.error("HTTP/2 Session error:", err.message);
});
session.on("frameError", (type, code, id) => {
console.error("HTTP/2 Frame error:", { type, code, id });
});
});
// サーバー全体のエラーハンドラー
server.on("error", (err) => {
console.error("Server error:", err.message);
});
server.on("stream", (stream, headers) => {
stream.respond({ ":status": 200 });
stream.end("OK");
});
server.listen(443);
Fastify での対策
Fastify を使用している場合の対策です。
javascriptconst fastify = require("fastify");
const fs = require("fs");
const app = fastify({
http2: true,
https: {
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
},
// Fastify のエラーハンドリングを有効化
disableRequestLogging: false,
});
// カスタムエラーハンドラー
app.setErrorHandler((error, request, reply) => {
console.error("Request error:", error);
reply.status(500).send({ error: "Internal Server Error" });
});
// HTTP/2 固有のエラーをハンドリング
app.server.on("secureConnection", (socket) => {
socket.on("error", (err) => {
console.error("TLS Socket error in Fastify:", err.message);
});
});
app.get("/", async () => {
return { status: "ok" };
});
app.listen({ port: 443, host: "0.0.0.0" });
リバースプロキシでの HTTP/2 終端
Node.js で HTTP/2 を直接処理せず、リバースプロキシで終端する方法です。
Nginx での設定
nginx# /etc/nginx/conf.d/app.conf
upstream nodejs {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# HTTP/2 は Nginx で終端
location / {
proxy_pass http://nodejs;
proxy_http_version 1.1; # Node.js には HTTP/1.1 で接続
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
AWS ALB での設定
yaml# CloudFormation テンプレート(抜粋)
Resources:
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Type: application
Scheme: internet-facing
HTTPSListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 443
Protocol: HTTPS
# ALB が HTTP/2 を終端し、ターゲットには HTTP/1.1 で接続
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Protocol: HTTP # HTTP/1.1
Port: 3000
ProtocolVersion: HTTP1
mermaidflowchart LR
subgraph internet["インターネット"]
client["クライアント"]
end
subgraph infra["インフラ"]
alb["ALB / Nginx<br/>HTTP/2 終端"]
node["Node.js<br/>HTTP/1.1"]
end
client -->|"HTTP/2"| alb
alb -->|"HTTP/1.1"| node
図の補足:リバースプロキシで HTTP/2 を終端し、Node.js には HTTP/1.1 で接続することで、脆弱性の影響を回避できます。
WAF でのペイロード検査
AWS WAF や Cloudflare WAF で、不正な HTTP/2 リクエストをブロックする設定です。
AWS WAF ルール例
json{
"Name": "BlockOversizedHeaders",
"Priority": 1,
"Statement": {
"SizeConstraintStatement": {
"FieldToMatch": {
"AllHeaders": {}
},
"ComparisonOperator": "GT",
"Size": 8192,
"TextTransformations": [
{
"Priority": 0,
"Type": "NONE"
}
]
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BlockOversizedHeaders"
}
}
つまずきポイント
- エラーハンドラーは
secureConnectionイベントで追加する必要があります。connectionイベントでは TLS 確立前なので効果がありません - リバースプロキシで HTTP/2 を終端する場合、HTTP/2 の恩恵(多重化など)はクライアント〜プロキシ間のみになります
- WAF のルールは、正常なリクエストを誤ブロックしないよう、閾値を慎重に設定してください
具体例:実務での対応パターン
この章でわかること
- 実際のプロジェクトでの対応手順
- 攻撃の検知方法
- 監視・アラートの設定
対応フローチャート
mermaidflowchart TD
start["脆弱性の認知"] --> check1{"HTTP/2 を<br/>直接使用?"}
check1 -->|"いいえ"| safe["影響なし"]
check1 -->|"はい"| check2{"Node.js<br/>22.22.0 以上?"}
check2 -->|"はい"| safe
check2 -->|"いいえ"| check3{"即時アップデート<br/>可能?"}
check3 -->|"はい"| update["Node.js<br/>アップデート"]
update --> verify["動作確認"]
verify --> safe
check3 -->|"いいえ"| mitigation["エラーハンドラー<br/>追加"]
mitigation --> proxy{"リバースプロキシ<br/>導入可能?"}
proxy -->|"はい"| addproxy["HTTP/2 終端を<br/>プロキシに移動"]
proxy -->|"いいえ"| schedule["アップデート<br/>計画策定"]
addproxy --> schedule
schedule --> update
図の補足:HTTP/2 を直接使用していない場合は影響を受けません。直接使用している場合は、Node.js のアップデートが最優先です。
実務での対応例:API サーバー
Day 1: 影響調査
bash# 1. Node.js バージョン確認
node -v
# v22.20.0(脆弱性あり)
# 2. HTTP/2 の使用確認
grep -rn "http2\|createSecureServer" --include="*.js" src/
# 3. プロセス内のリスナー確認
node -e "
const server = require('./src/server');
console.log('secureConnection listeners:',
server.listeners('secureConnection').length);
"
Day 1: 緊急対応(エラーハンドラー追加)
javascript// src/server.js への追加
const server = http2.createSecureServer(options);
// 緊急追加: TLSSocket エラーハンドラー
server.on("secureConnection", (socket) => {
socket.on("error", (err) => {
console.error(
JSON.stringify({
level: "error",
type: "tls_socket_error",
message: err.message,
code: err.code,
remoteAddress: socket.remoteAddress,
timestamp: new Date().toISOString(),
}),
);
});
});
Day 2: 監視・アラートの設定
javascript// CloudWatch Logs への送信(AWS SDK v3)
const {
CloudWatchLogsClient,
PutLogEventsCommand,
} = require("@aws-sdk/client-cloudwatch-logs");
const client = new CloudWatchLogsClient({ region: "ap-northeast-1" });
server.on("secureConnection", (socket) => {
socket.on("error", async (err) => {
// CloudWatch Logs に送信
await client.send(
new PutLogEventsCommand({
logGroupName: "/app/http2-errors",
logStreamName: `${process.env.HOSTNAME}`,
logEvents: [
{
timestamp: Date.now(),
message: JSON.stringify({
type: "tls_socket_error",
message: err.message,
code: err.code,
remoteAddress: socket.remoteAddress,
}),
},
],
}),
);
// 攻撃の可能性がある場合はアラート
if (err.code === "ECONNRESET" || err.message.includes("HPACK")) {
console.error("ALERT: Possible CVE-2025-59465 attack detected");
}
});
});
Day 3-4: Node.js アップデート
bash# 1. ローカルでアップデート検証
nvm install 22.22.0
nvm use 22.22.0
npm test
# 2. ステージング環境でデプロイ
kubectl set image deployment/api api=node:22.22.0-alpine --namespace=staging
# 3. 負荷テスト
ab -n 10000 -c 100 https://staging.example.com/
# 4. 本番適用
kubectl set image deployment/api api=node:22.22.0-alpine --namespace=production
攻撃の検知方法
ログベースの検知
javascript// 攻撃パターンの検知
const ATTACK_PATTERNS = [
/ECONNRESET/,
/HPACK/,
/PROTOCOL_ERROR/,
/COMPRESSION_ERROR/,
];
function isAttackAttempt(error) {
return ATTACK_PATTERNS.some((pattern) => pattern.test(error.message));
}
server.on("secureConnection", (socket) => {
socket.on("error", (err) => {
if (isAttackAttempt(err)) {
// 攻撃の可能性あり
console.error(
JSON.stringify({
level: "warn",
type: "possible_attack",
message: err.message,
remoteAddress: socket.remoteAddress,
timestamp: new Date().toISOString(),
}),
);
// Slack 通知などのアラートを送信
sendAlert({
title: "CVE-2025-59465 攻撃の可能性",
message: `IP: ${socket.remoteAddress}, Error: ${err.message}`,
});
}
});
});
メトリクスベースの検知
javascript// Prometheus メトリクス
const promClient = require("prom-client");
const http2ErrorCounter = new promClient.Counter({
name: "http2_tls_errors_total",
help: "Total HTTP/2 TLS errors",
labelNames: ["error_code", "remote_address_prefix"],
});
server.on("secureConnection", (socket) => {
socket.on("error", (err) => {
const addressPrefix =
socket.remoteAddress?.split(".").slice(0, 2).join(".") || "unknown";
http2ErrorCounter.inc({
error_code: err.code || "UNKNOWN",
remote_address_prefix: addressPrefix,
});
});
});
CI/CD での自動チェック
yaml# .github/workflows/security-check.yml
name: HTTP/2 Security Check
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
check-http2-handlers:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for HTTP/2 usage
id: http2-check
run: |
if grep -rn "http2\|createSecureServer" --include="*.js" --include="*.ts" src/; then
echo "http2_used=true" >> $GITHUB_OUTPUT
else
echo "http2_used=false" >> $GITHUB_OUTPUT
fi
- name: Check for error handlers
if: steps.http2-check.outputs.http2_used == 'true'
run: |
# secureConnection イベントでのエラーハンドラーをチェック
if ! grep -rn "secureConnection.*socket.*on.*error" --include="*.js" --include="*.ts" src/; then
echo "::warning::HTTP/2 を使用していますが、TLSSocket のエラーハンドラーが見つかりません"
echo "CVE-2025-59465 の影響を受ける可能性があります"
fi
- name: Check Node.js version
run: |
NODE_VERSION=$(node -v | sed 's/v//')
echo "Current Node.js version: $NODE_VERSION"
# バージョンチェックロジック...
つまずきポイント
- エラーハンドラーを追加しても、ログが大量に出力される可能性があります。レートリミットを設定してください
- 攻撃の検知は誤検知の可能性もあるため、アラートの閾値を適切に設定してください
- 本番適用前に、必ずステージング環境で動作確認を行ってください
HTTP/2 構成パターン比較(実務判断用・詳細)
この章でわかること
- 各構成パターンでの脆弱性影響
- 推奨される構成
- 長期的なアーキテクチャ判断
詳細比較表
| 構成 | HTTP/2 処理 | 脆弱性影響 | パフォーマンス | 推奨度 |
|---|---|---|---|---|
| Node.js 直接(HTTP/2) | Node.js | あり | 高 | 低(要対策) |
| Nginx → Node.js(HTTP/1.1) | Nginx | なし | 中 | 高 |
| Nginx → Node.js(HTTP/2) | 両方 | あり | 高 | 低 |
| ALB → Node.js(HTTP/1.1) | ALB | なし | 中 | 高 |
| CloudFront → ALB → Node.js | CloudFront/ALB | なし | 高 | 最高 |
構成別の詳細解説
構成1: Node.js 直接(HTTP/2)
クライアント ─── HTTP/2 ─── Node.js
- メリット: シンプル、低レイテンシー
- デメリット: 脆弱性の影響を直接受ける
- 推奨: 開発環境のみ。本番環境では非推奨
構成2: Nginx → Node.js(HTTP/1.1)
swiftクライアント ─── HTTP/2 ─── Nginx ─── HTTP/1.1 ─── Node.js
- メリット: Node.js は脆弱性の影響を受けない
- デメリット: HTTP/2 の多重化の恩恵がバックエンドに届かない
- 推奨: 多くの本番環境で推奨
構成3: CloudFront → ALB → Node.js
swiftクライアント ─── HTTP/2 ─── CloudFront ─── HTTP/2 ─── ALB ─── HTTP/1.1 ─── Node.js
- メリット: 最も安全、CDN のキャッシュも活用可能
- デメリット: 構成が複雑、コストが高い
- 推奨: 大規模サービスで推奨
移行パス
現在 Node.js で直接 HTTP/2 を処理している場合の移行パスです。
mermaidflowchart TD
current["現状: Node.js 直接 HTTP/2"] --> assess{"即時アップデート<br/>可能?"}
assess -->|"はい"| update["Node.js アップデート"]
assess -->|"いいえ"| handler["エラーハンドラー追加<br/>(暫定対策)"]
handler --> plan["構成見直し検討"]
update --> plan
plan --> q1{"トラフィック量"}
q1 -->|"少"| nginx["Nginx 追加"]
q1 -->|"中"| alb["ALB 導入"]
q1 -->|"多"| cdn["CloudFront + ALB"]
nginx --> done["移行完了"]
alb --> done
cdn --> done
図の補足:トラフィック量に応じて、適切な構成を選択してください。
つまずきポイント
- Nginx を追加する場合、既存の TLS 証明書の管理方法を見直す必要があります
- ALB は HTTP/2 を終端しますが、WebSocket は HTTP/1.1 で処理されます
- CloudFront を使用する場合、キャッシュの設定を適切に行わないと、動的コンテンツに問題が発生する可能性があります
まとめ
CVE-2025-59465 は、Node.js で HTTP/2 を直接処理しているすべての環境に影響する深刻な脆弱性です。
-
1リクエストでサーバーがクラッシュ:不正な HPACK データを含む HEADERS フレームを受信するだけで、認証不要でサーバーをダウンさせられます。
-
Node.js のアップデートが根本解決:20.20.0、22.22.0、24.13.0、25.3.0 以降で修正されています。
-
エラーハンドラーの追加で暫定対策可能:
secureConnectionイベントで TLSSocket のエラーをハンドリングすることで、クラッシュを防げます。 -
リバースプロキシでの HTTP/2 終端が推奨:Nginx や ALB で HTTP/2 を終端し、Node.js には HTTP/1.1 で接続する構成が、セキュリティと運用の両面で優れています。
-
攻撃の検知と監視が重要:エラーハンドラーを追加した場合も、攻撃の試行を検知するためのログ出力とアラート設定を行ってください。
HTTP/2 は高速で効率的なプロトコルですが、その複雑さゆえに脆弱性も発見されやすくなっています。本番環境では、Node.js で直接 HTTP/2 を処理するのではなく、リバースプロキシで終端する構成を検討してください。
関連リンク
- Node.js Security Releases - 2026年1月(公式)
- Node.js HTTP/2 ドキュメント(公式)
- RFC 7540 - HTTP/2(IETF)
- RFC 7541 - HPACK(IETF)
- Nginx HTTP/2 設定(公式)
- AWS ALB HTTP/2 サポート(公式)
著書
articleNode.jsセキュリティアップデート、今すぐ必要?環境別の判断フローチャート
articleNode.js HTTP/2サーバーが1リクエストでダウン:CVE-2025-59465の攻撃手法と防御策
articleDatadog・New Relic利用者は要注意:async_hooksの脆弱性がAPMツール経由でDoSを引き起こす理由
article【緊急】2026年1月13日発表 Node.js 脆弱性8件の詳細と対策|HTTP/2・async_hooks のDoS問題を解説
article2026年1月11日Node.jsとTypeScriptのユースケース バックエンド開発で型を活かす実践テクニック
article2025年12月24日TypeScriptでESMとCJS混在をトラブルシュートする ERR_REQUIRE_ESMとimport不可を直す
articleNode.jsセキュリティアップデート、今すぐ必要?環境別の判断フローチャート
articleNode.js HTTP/2サーバーが1リクエストでダウン:CVE-2025-59465の攻撃手法と防御策
articleDatadog・New Relic利用者は要注意:async_hooksの脆弱性がAPMツール経由でDoSを引き起こす理由
articleNext.js・React Server Componentsが危険?async_hooksの脆弱性CVE-2025-59466を徹底解説
article【緊急】2026年1月13日発表 Node.js 脆弱性8件の詳細と対策|HTTP/2・async_hooks のDoS問題を解説
article2026年1月13日TypeScriptで既存コードを型安全化する使い方 段階的リファクタリング手順とチェックポイント
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来
