T-CREATOR

<div />

Datadog・New Relic利用者は要注意:async_hooksの脆弱性がAPMツール経由でDoSを引き起こす理由

Datadog・New Relic利用者は要注意:async_hooksの脆弱性がAPMツール経由でDoSを引き起こす理由

Datadog や New Relic などの APM ツールを導入している Node.js 環境で、try-catch が効かずサーバーがクラッシュする深刻な脆弱性(CVE-2025-59466)が発見されました。本記事では、APM ツールがなぜ脆弱性の引き金になるのか、各ツールでの対策方法を詳しく解説します。

「APM ツールとセキュリティ」「Datadog 導入環境の脆弱性対策」「async_hooks の安全な使い方」を知りたい方に向けた記事です。

APM ツール別・脆弱性影響の比較表

#APM ツールasync_hooks 使用方法脆弱性影響一時無効化対応優先度
1Datadog(dd-trace)createHook 直接あり可能最優先
2New ReliccreateHook 直接あり可能最優先
3DynatracecreateHook 直接あり可能最優先
4Elastic APMcreateHook 直接あり可能最優先
5OpenTelemetrycreateHook 直接あり可能最優先
6Sentry(Performance)AsyncLocalStorage 経由あり可能
7APM なし使用なしなし-

この表は即答用です。各 APM ツールでの詳細な影響と対策は後段で解説します。

検証環境

  • OS: macOS Sonoma 14.7 / Ubuntu 22.04 LTS
  • Node.js: 22.21.0(脆弱性確認)→ 22.22.0(修正確認)
  • 主要パッケージ:
    • dd-trace: 5.30.0
    • newrelic: 12.8.0
    • @opentelemetry/sdk-node: 0.57.0
    • @opentelemetry/auto-instrumentations-node: 0.54.0
    • @elastic/apm-node: 4.9.0
    • @sentry/node: 8.47.0
  • 検証日: 2026年1月15日

背景:APM ツールが async_hooks を使用する理由

この章でわかること

  • APM ツールの基本的な仕組み(初学者向け)
  • なぜ APM が async_hooks を必要とするのか
  • 脆弱性との関係性

APM ツールの基本(初学者向け)

APM(Application Performance Monitoring)ツールは、アプリケーションのパフォーマンスとエラーを監視するためのソフトウェアです。

主な機能:

  • 分散トレーシング: リクエストが複数のサービスを経由する様子を追跡
  • エラー監視: 例外やエラーの発生を検知・通知
  • パフォーマンス計測: レスポンスタイム、スループットの可視化
javascript// Datadog APM の初期化例
const tracer = require("dd-trace").init({
  service: "my-api",
  env: "production",
});

// この1行を追加するだけで、すべてのHTTPリクエストが追跡される

APM が async_hooks を必要とする理由

Node.js はシングルスレッド・非同期で動作するため、「どのリクエストがどの処理を実行しているか」を追跡するのが困難です。APM ツールは async_hooks を使用して、非同期処理のコンテキストを追跡しています。

以下の図は、APM ツールがリクエストを追跡する仕組みを示しています。

mermaidsequenceDiagram
  participant Client as クライアント
  participant Server as Node.js サーバー
  participant APM as APM(async_hooks)
  participant DB as データベース

  Client->>Server: HTTPリクエスト
  APM->>APM: コンテキスト作成(ID: 123)
  Server->>DB: クエリ実行
  APM->>APM: ID: 123 に紐付け
  DB-->>Server: 結果返却
  APM->>APM: ID: 123 のスパン終了
  Server-->>Client: レスポンス
  APM->>APM: トレース送信

図の補足:APM ツールは async_hooks を使って、リクエストごとに一意のコンテキストを作成し、すべての非同期処理を紐付けています。

async_hooks の内部動作

javascriptimport { createHook } from "node:async_hooks";

// APM ツールが内部で行っている処理(簡略化)
const contexts = new Map();

const hook = createHook({
  init(asyncId, type, triggerAsyncId) {
    // 非同期処理が開始されたとき
    // 親のコンテキストを継承
    if (contexts.has(triggerAsyncId)) {
      contexts.set(asyncId, contexts.get(triggerAsyncId));
    }
  },
  destroy(asyncId) {
    // 非同期処理が終了したとき
    contexts.delete(asyncId);
  },
});

hook.enable(); // ← この時点で脆弱性の影響を受ける状態になる

脆弱性が発生するメカニズム

問題は、async_hooks が有効な状態でスタックオーバーフローが発生した場合です。

mermaidflowchart LR
  subgraph normal["APM なし"]
    A1["再帰処理"] --> A2["スタック枯渇"]
    A2 --> A3["RangeError"]
    A3 --> A4["try-catch で<br/>キャッチ可能"]
  end

  subgraph vuln["APM あり"]
    B1["再帰処理"] --> B2["スタック枯渇"]
    B2 --> B3["async_hooks<br/>内でエラー"]
    B3 --> B4["致命的エラー<br/>として処理"]
    B4 --> B5["プロセス終了<br/>(終了コード7)"]
  end

図の補足:APM ツールが有効な場合、スタックオーバーフローは「致命的フックエラー」として扱われ、try-catch をバイパスしてプロセスが終了します。

つまずきポイント

  • APM ツールを「import するだけ」で async_hooks が有効化され、脆弱性の影響を受けます
  • 「APM は監視だけだから影響ないはず」という認識は誤りです
  • APM を無効化しない限り、Node.js をアップデートするまで脆弱性は解消されません

課題:各 APM ツールでの脆弱性の詳細

この章でわかること

  • 各 APM ツールでの脆弱性の発現条件
  • 攻撃シナリオ
  • 影響範囲の確認方法

脆弱性の技術的詳細

項目内容
CVE番号CVE-2025-59466
深刻度MEDIUM(ただし APM 利用環境では実質 HIGH)
影響バージョンNode.js 20.x〜25.x(修正前)
修正バージョン20.20.0, 22.22.0, 24.13.0, 25.3.0
攻撃種別リモート DoS(サービス拒否)
認証要否不要

Datadog(dd-trace)での影響

Datadog の Node.js APM(dd-trace)は、初期化時に async_hooks.createHook() を呼び出します。

javascript// dd-trace の初期化(これだけで脆弱性の影響を受ける)
require("dd-trace").init();

// 以降、すべてのコードが async_hooks 有効状態で実行される

影響を受けるコードパターン

javascript// 脆弱なコード例(Express + Datadog)
const express = require("express");
require("dd-trace").init(); // ← async_hooks 有効化

const app = express();
app.use(express.json());

app.post("/api/process", (req, res) => {
  try {
    const result = processNestedData(req.body);
    res.json({ success: true, result });
  } catch (err) {
    // このキャッチブロックは実行されない
    res.status(500).json({ error: err.message });
  }
});

function processNestedData(data) {
  if (Array.isArray(data)) {
    return data.map((item) => processNestedData(item));
  }
  if (typeof data === "object" && data !== null) {
    return Object.fromEntries(
      Object.entries(data).map(([k, v]) => [k, processNestedData(v)]),
    );
  }
  return data;
}

確認方法

bash# Datadog が async_hooks を使用しているか確認
node -e "
  const original = require('async_hooks').createHook;
  require('async_hooks').createHook = function(...args) {
    console.log('async_hooks.createHook called');
    return original.apply(this, args);
  };
  require('dd-trace').init();
"
# 出力: async_hooks.createHook called

New Relic での影響

New Relic も同様に async_hooks を使用しています。

javascript// New Relic の初期化
require("newrelic"); // ← async_hooks 有効化

const express = require("express");
const app = express();

// 以降、同様の脆弱性が発生

New Relic 固有の注意点

New Relic は newrelic.js 設定ファイルまたは環境変数で初期化されるため、コード上で明示的に require していなくても有効化されている場合があります。

javascript// newrelic.js(プロジェクトルートに配置)
exports.config = {
  app_name: ["My Application"],
  license_key: "your_license_key",
  logging: {
    level: "info",
  },
};
bash# 環境変数で有効化されているケース
NEW_RELIC_LICENSE_KEY=xxx node app.js

OpenTelemetry での影響

OpenTelemetry は、ベンダーに依存しないオープンソースの Observability フレームワークです。

javascript// OpenTelemetry の初期化
const { NodeSDK } = require("@opentelemetry/sdk-node");
const {
  getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");

const sdk = new NodeSDK({
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start(); // ← async_hooks 有効化

OpenTelemetry の利点

OpenTelemetry は、APM バックエンド(Datadog、Jaeger、Zipkin など)を切り替えられるため、ベンダーロックインを避けられます。ただし、脆弱性の影響は他の APM ツールと同様です。

影響範囲の確認方法

自身の環境が影響を受けるか確認するスクリプトです。

javascript// scripts/check-apm-vulnerability.js
const asyncHooks = require("async_hooks");

// createHook の呼び出しを監視
const originalCreateHook = asyncHooks.createHook;
let hookCalled = false;

asyncHooks.createHook = function (...args) {
  hookCalled = true;
  console.log("⚠️  async_hooks.createHook が呼び出されました");
  console.log("   呼び出し元:", new Error().stack.split("\n")[2]);
  return originalCreateHook.apply(this, args);
};

// アプリケーションのエントリーポイントを読み込む
require("./app.js");

setTimeout(() => {
  if (hookCalled) {
    console.log("\n❌ このアプリケーションは CVE-2025-59466 の影響を受けます");
    console.log("   Node.js を 22.22.0 以上にアップデートしてください");
  } else {
    console.log("\n✅ async_hooks は使用されていません");
  }
}, 1000);

つまずきポイント

  • APM の初期化コードがエントリーポイントの最初に配置されていることが多く、見落としやすいです
  • Docker イメージやインフラ側で APM が有効化されているケースもあります
  • 本番環境と開発環境で APM の設定が異なる場合、開発環境では再現しないことがあります

解決策と判断:APM ツール別の対策

この章でわかること

  • 推奨される対策の優先順位
  • 各 APM ツールでの一時無効化方法
  • Node.js アップデートの手順

対策の優先順位

優先度対策効果トレードオフ
1(最優先)Node.js アップデート根本解決テスト工数
2入力データのネスト深度制限攻撃緩和実装工数
3APM 一時無効化攻撃回避監視機能喪失
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 の場合
# Dockerfile
FROM node:22.22.0-alpine

Datadog(dd-trace)の一時無効化

方法1: 環境変数で無効化

bash# APM を完全に無効化
DD_TRACE_ENABLED=false node app.js

# または、トレーシングのみ無効化(メトリクスは継続)
DD_TRACE_ENABLED=false DD_RUNTIME_METRICS_ENABLED=true node app.js

方法2: コードで条件付き初期化

javascript// app.js
if (process.env.ENABLE_APM === "true") {
  require("dd-trace").init({
    service: "my-api",
    env: process.env.NODE_ENV,
  });
}

// 以降の通常のコード
const express = require("express");
// ...

方法3: Datadog Agent 側で無効化

yaml# datadog.yaml(Agent 設定)
apm_config:
  enabled: false

New Relic の一時無効化

方法1: 環境変数で無効化

bash# APM を完全に無効化
NEW_RELIC_ENABLED=false node app.js

方法2: 設定ファイルで無効化

javascript// newrelic.js
exports.config = {
  agent_enabled: process.env.ENABLE_APM === "true",
  app_name: ["My Application"],
  // ...
};

方法3: コードで条件付き読み込み

javascript// app.js
if (process.env.ENABLE_APM === "true") {
  require("newrelic");
}

OpenTelemetry の一時無効化

方法1: SDK を起動しない

javascript// instrumentation.js
const { NodeSDK } = require("@opentelemetry/sdk-node");

const sdk = new NodeSDK({
  // 設定...
});

if (process.env.ENABLE_APM === "true") {
  sdk.start();
  console.log("OpenTelemetry started");
} else {
  console.log("OpenTelemetry disabled");
}

方法2: 環境変数で無効化

bash# OpenTelemetry の自動計装を無効化
OTEL_SDK_DISABLED=true node app.js

入力データのネスト深度制限(暫定対策)

APM を無効化せずに攻撃を緩和する方法です。

javascript// middleware/validateDepth.js
function validateJsonDepth(obj, maxDepth = 50, currentDepth = 0) {
  if (currentDepth > maxDepth) {
    throw new Error("JSON nesting depth exceeds limit");
  }

  if (obj === null || typeof obj !== "object") {
    return true;
  }

  const values = Array.isArray(obj) ? obj : Object.values(obj);
  for (const value of values) {
    validateJsonDepth(value, maxDepth, currentDepth + 1);
  }

  return true;
}

// Express ミドルウェアとして使用
function depthLimitMiddleware(maxDepth = 50) {
  return (req, res, next) => {
    if (req.body && typeof req.body === "object") {
      try {
        validateJsonDepth(req.body, maxDepth);
        next();
      } catch (err) {
        res.status(400).json({ error: err.message });
      }
    } else {
      next();
    }
  };
}

module.exports = { validateJsonDepth, depthLimitMiddleware };
javascript// app.js での使用例
const express = require("express");
require("dd-trace").init(); // APM は有効のまま

const { depthLimitMiddleware } = require("./middleware/validateDepth");

const app = express();
app.use(express.json());
app.use(depthLimitMiddleware(50)); // ← 深度制限を追加

app.post("/api/process", (req, res) => {
  // ここに到達する時点で、深いネストは拒否されている
  const result = processNestedData(req.body);
  res.json({ success: true, result });
});

つまずきポイント

  • APM を無効化すると、その間のエラーやパフォーマンス問題を検知できなくなります
  • 暫定対策の validateJsonDepth 関数自体も再帰処理なので、maxDepth は 50 程度に抑える必要があります
  • 複数の APM ツールを併用している場合、すべてを無効化する必要があります

具体例:実務での対応パターン

この章でわかること

  • 実際のプロジェクトでの対応手順
  • APM 無効化による監視ギャップへの対処
  • 段階的な対応計画

対応フローチャート

mermaidflowchart TD
  start["脆弱性の認知"] --> check1{"Node.js バージョン<br/>確認"}
  check1 -->|"修正済み"| done["対応完了"]
  check1 -->|"未修正"| check2{"即時アップデート<br/>可能?"}
  check2 -->|"可能"| update["Node.js<br/>アップデート"]
  update --> verify["APM 動作確認"]
  verify --> done
  check2 -->|"不可"| assess{"リスク評価"}
  assess -->|"高リスク"| disable["APM 一時無効化"]
  assess -->|"中リスク"| mitigation["入力バリデーション<br/>追加"]
  disable --> altmon["代替監視<br/>(ログ、外形監視)"]
  altmon --> schedule["アップデート<br/>計画策定"]
  mitigation --> schedule
  schedule --> update

図の補足:リスク評価に基づいて、APM 無効化か入力バリデーションかを選択します。

実務での対応例:EC サイト(Datadog 導入環境)

実際に業務で対応した EC サイト(Express + Datadog)での事例です。

Day 1: 影響調査

bash# 1. Node.js バージョン確認
node -v
# v22.20.0(脆弱性あり)

# 2. Datadog の使用確認
grep -r "dd-trace" package.json
# "dd-trace": "^5.30.0"

# 3. 再帰処理を含むエンドポイントの洗い出し
grep -rn "function.*(" --include="*.js" src/routes/

Day 1: 緊急対応(APM 一時無効化)

bash# 本番環境の環境変数を変更
# Kubernetes の場合
kubectl set env deployment/api DD_TRACE_ENABLED=false

# または ConfigMap を更新
kubectl edit configmap api-config

Day 1: 代替監視の設定

APM を無効化している間、以下の代替手段で監視を継続しました。

javascript// 簡易的なリクエストログ
const morgan = require("morgan");

app.use(
  morgan(":method :url :status :response-time ms", {
    stream: {
      write: (message) => {
        // CloudWatch Logs や Datadog Logs に送信
        console.log(
          JSON.stringify({ type: "access", message: message.trim() }),
        );
      },
    },
  }),
);

// エラーログ
app.use((err, req, res, next) => {
  console.error(
    JSON.stringify({
      type: "error",
      error: err.message,
      stack: err.stack,
      path: req.path,
      method: req.method,
    }),
  );
  res.status(500).json({ error: "Internal Server Error" });
});

Day 2-3: Node.js アップデート準備

bash# 1. ローカルでアップデート検証
nvm install 22.22.0
nvm use 22.22.0

# 2. テスト実行
npm test

# 3. E2E テスト
npm run test:e2e

# 4. ステージング環境でデプロイ・検証

Day 4: 本番適用

bash# 1. Node.js アップデート(Docker イメージ更新)
docker build -t api:22.22.0 .
docker push api:22.22.0

# 2. Kubernetes でローリングアップデート
kubectl set image deployment/api api=api:22.22.0

# 3. APM 再有効化
kubectl set env deployment/api DD_TRACE_ENABLED=true

# 4. 動作確認
kubectl logs -f deployment/api | grep "dd-trace"

CI/CD での自動チェック

yaml# .github/workflows/security-check.yml
name: Security Check

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  check-vulnerability:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: ".nvmrc"

      - name: Check Node.js version for CVE-2025-59466
        run: |
          NODE_VERSION=$(node -v | sed 's/v//')
          echo "Current Node.js version: $NODE_VERSION"

          # バージョン比較(簡易版)
          MAJOR=$(echo $NODE_VERSION | cut -d. -f1)
          MINOR=$(echo $NODE_VERSION | cut -d. -f2)
          PATCH=$(echo $NODE_VERSION | cut -d. -f3)

          VULNERABLE=false
          if [ "$MAJOR" -eq 22 ] && [ "$MINOR" -lt 22 ]; then
            VULNERABLE=true
          elif [ "$MAJOR" -eq 20 ] && [ "$MINOR" -lt 20 ]; then
            VULNERABLE=true
          fi

          if [ "$VULNERABLE" = true ]; then
            echo "::error::Node.js $NODE_VERSION is vulnerable to CVE-2025-59466"
            exit 1
          fi

      - name: Check for APM usage
        run: |
          if grep -q "dd-trace\|newrelic\|@opentelemetry" package.json; then
            echo "APM tool detected. Ensure Node.js is updated."
          fi

監視ギャップへの対処

APM を無効化している間の監視ギャップに対処するためのチェックリストです。

監視項目APM代替手段
エラー率CloudWatch Logs + アラーム
レスポンスタイム外形監視(Synthetics)
スループットALB メトリクス
分散トレーシング❌(一時的に喪失)
データベースクエリRDS Performance Insights

つまずきポイント

  • APM を無効化する際は、必ず代替監視を先に設定してください
  • ローリングアップデート中は、新旧バージョンが混在するため、両方のログを確認する必要があります
  • APM 再有効化後、トレースが正常に送信されているか確認を忘れずに

APM ツール比較(実務判断用・詳細)

この章でわかること

  • 各 APM ツールの脆弱性対応状況
  • 長期的な APM 選定の観点
  • ベンダーロックインの回避策

詳細比較表

APM ツール脆弱性影響無効化の容易さ公式対応情報OpenTelemetry 互換
Datadog(dd-trace)あり環境変数で可能ドキュメントありエクスポーター対応
New Relicあり環境変数で可能ドキュメントありエクスポーター対応
Dynatraceあり設定で可能ドキュメントあり限定的
Elastic APMあり環境変数で可能ドキュメントありネイティブ対応
OpenTelemetryありSDK 停止で可能GitHub Issue-
SentryありDSN 削除で可能ドキュメントあり限定的

長期的な観点:OpenTelemetry への移行

今回の脆弱性を機に、ベンダー固有の APM から OpenTelemetry への移行を検討する組織も増えています。

OpenTelemetry のメリット

  1. ベンダーロックイン回避: バックエンドを自由に切り替え可能
  2. 標準化: CNCF 主導の業界標準
  3. 将来性: 主要クラウドベンダーがサポート

移行パス

javascript// Before: Datadog 固有
require("dd-trace").init();

// After: OpenTelemetry + Datadog エクスポーター
const { NodeSDK } = require("@opentelemetry/sdk-node");
const {
  getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");
const {
  OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-http");

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: "https://trace.agent.datadoghq.com/v0.4/traces",
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

APM 選定時のセキュリティ観点

今後の APM 選定では、以下の観点も考慮することをお勧めします。

観点チェック項目
脆弱性対応セキュリティアドバイザリの公開頻度・速度
無効化の容易さ環境変数のみで無効化可能か
依存関係async_hooks 以外の低レベル API への依存
サポートNode.js の新バージョン対応の速度

つまずきポイント

  • OpenTelemetry に移行しても、async_hooks を使用する点は変わらないため、脆弱性の影響は受けます
  • APM のバックエンドを変更する場合、ダッシュボードやアラートの再設定が必要です
  • 移行期間中は、新旧両方の APM でデータを収集し、差異がないか確認してください

まとめ

CVE-2025-59466 は、Datadog、New Relic、OpenTelemetry などの APM ツールを導入しているほぼすべての Node.js 環境に影響する深刻な脆弱性です。

  1. APM ツールは async_hooks を使用する:分散トレーシングやコンテキスト追跡のために、すべての主要 APM ツールが async_hooks を使用しています。これにより、APM を導入するだけで脆弱性の影響を受ける状態になります。

  2. Node.js のアップデートが根本解決:20.20.0、22.22.0、24.13.0、25.3.0 以降で修正されています。APM ツール側の対応を待つ必要はありません。

  3. APM の一時無効化は有効だがトレードオフあり:環境変数で簡単に無効化できますが、監視機能が失われます。代替監視を設定した上で実施してください。

  4. 入力バリデーションで攻撃を緩和可能:APM を有効にしたまま、ネスト深度制限のミドルウェアを追加することで攻撃を緩和できます。ただし、根本解決ではありません。

  5. 長期的には OpenTelemetry への移行も検討:ベンダーロックインを回避し、将来の脆弱性対応を柔軟にするために、OpenTelemetry への移行も選択肢です。

APM ツールは本番環境の可観測性を支える重要なコンポーネントです。今回の脆弱性を機に、APM の依存関係とセキュリティ対応体制を見直すことをお勧めします。

関連リンク

著書

とあるクリエイター

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

;