T-CREATOR

<div />

Node.js HTTP/2サーバーが1リクエストでダウン:CVE-2025-59465の攻撃手法と防御策

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 使用エラーハンドラー攻撃時の動作緊急度
1Node.js 直接 + HTTP/2ありなし即座にクラッシュ最優先
2Node.js 直接 + HTTP/2ありあり接続切断のみ低(対策済み)
3Nginx → Node.js(HTTP/1.1)Nginx のみ-影響なし
4ALB → Node.js(HTTP/1.1)ALB のみ-影響なし
5Fastify + HTTP/2ありフレームワーク依存要確認
6Express + 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 は以下の仕組みで動作します。

  1. 静的テーブル: よく使われるヘッダー(:method:path など)を事前定義
  2. 動的テーブル: 通信中に使用されたヘッダーをキャッシュ
  3. ハフマン符号化: 文字列を圧縮
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 フレームは、以下の特徴を持ちます。

  1. オーバーサイズの HPACK データ: 動的テーブルのサイズ制限を超えるデータ
  2. 無効なハフマン符号: デコード不能な文字列
  3. 不正なインデックス参照: 存在しないテーブルエントリへの参照
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 終端攻撃回避
4WAF でのペイロード検査攻撃検知・ブロック中〜高

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.jsCloudFront/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. 1リクエストでサーバーがクラッシュ:不正な HPACK データを含む HEADERS フレームを受信するだけで、認証不要でサーバーをダウンさせられます。

  2. Node.js のアップデートが根本解決:20.20.0、22.22.0、24.13.0、25.3.0 以降で修正されています。

  3. エラーハンドラーの追加で暫定対策可能secureConnection イベントで TLSSocket のエラーをハンドリングすることで、クラッシュを防げます。

  4. リバースプロキシでの HTTP/2 終端が推奨:Nginx や ALB で HTTP/2 を終端し、Node.js には HTTP/1.1 で接続する構成が、セキュリティと運用の両面で優れています。

  5. 攻撃の検知と監視が重要:エラーハンドラーを追加した場合も、攻撃の試行を検知するためのログ出力とアラート設定を行ってください。

HTTP/2 は高速で効率的なプロトコルですが、その複雑さゆえに脆弱性も発見されやすくなっています。本番環境では、Node.js で直接 HTTP/2 を処理するのではなく、リバースプロキシで終端する構成を検討してください。

関連リンク

著書

とあるクリエイター

フロントエンドエンジニア Next.js / React / TypeScript / Node.js / Docker / AI Coding

;