Datadog RUM & Session Replay 完全防御マニュアル:クライアントサイド難読化の極限最適化
オブザーバビリティの究極形は、「ユーザー体験の完全な可視化」と「プライバシーの絶対的な死守」という、一見して矛盾する二律背反の調停にある。
Session Replayは、DOMの変異(Mutation)をミリ秒単位でキャプチャし、あたかもビデオのように再現する魔術的なツールだ。しかし、その裏で何が起きているか? ログインフォームのパスワード、クレジットカードのCVV、セッションアクセストークン、あるいは個人的なチャットの内容——これらが暗号化されずにペイロードに乗れば、一瞬にして法規制違反(GDPR、CCPA、APPI)および致命的なセキュリティインシデントへと直結する。
デフォルトのマスク機能に依存しているうちは、ジュニアエンジニアの域を出ない。本稿では、Datadog RUMとSession Replayの内部アーキテクチャを解剖し、パフォーマンスを1バイトたりとも犠牲にせず、PⅡ(個人識別情報)を完全にハント・無害化するクライアントサイド難読化の極限設定を叩き込む。
—
1. 内部アーキテクチャの理解:なぜデフォルト設定では不十分なのか
DatadogのSession Replayは、ブラウザ上でDOMツリーをシリアライズし、`rrweb`をベースにしたエンジンで差分をDatadogへストリーミングしている。
ここで知るべき最大のハードウェア/ネットワークの制約は、「難読化(Masking)はサーバーサイドではなく、クライアントサイド(ブラウザのメインスレッド)で実行される」という点だ。
[DOM Mutation] -> [Client-side Masking (CPU)] -> [Payload Compression] -> [HTTPS POST to Datadog]
↑
ここに負荷をかけすぎるとメインスレッドがブロックされ、
FID (First Input Delay) や INP (Interaction to Next Paint) が悪化する
正規表現ベースの総当たりマスクはブラウザを殺す。CSSセレクターと属性を活用した「O(1)に近い特定要素の直撃マスク」こそが唯一の正解だ。
—
2. マスク戦略のレイヤー構造
Datadogはデフォルトで以下のマスクレベルを提供している。
1. `mask-all-inputs` (デフォルト): すべての入力フィールド(``, `
しかし、現代のシングルページアプリケーション(SPA)において、入力フィールド以外(例えば、ReactやVueで構築されたカスタムモーダル内のテキスト、URLパラメータ、`data-`属性に埋め込まれたユーザーIDなど)にPⅡが露出するケースは枚挙に暇がない。
したがって、我々は「グローバルデフォルトの厳格化」と「カスタムCSSセレクターによる外科的手術」を組み合わせる必要がある。
—
3. 実装コード:極限まで最適化された初期化設定
以下のコードは、パフォーマンスの劣化を最小限に抑えつつ、鉄壁のプライバシー保護を実現するDatadog RUMの初期化設定(TypeScript)である。
import { datadogRum } from ‘@datadog/browser-rum’;
datadogRum.init({
applicationId: ‘YOUR_DATADOG_APP_ID’,
clientToken: ‘YOUR_DATADOG_CLIENT_TOKEN’,
site: ‘datadoghq.com’,
service: ‘fintech-core-web’,
env: ‘production’,
version: ‘1.4.2’,
// RUMサンプリングレート(100%はコストとCPUの無駄。プロダクションでは適正値を設定)
sessionSampleRate: 100,
sessionReplaySampleRate: 20, // リプレイは20%に絞りつつ、エラー時は強制キャプチャ
trackInteractions: true,
trackResources: true,
trackLongTasks: true,
// === Session Replay プライバシー防御の神髄 ===
defaultPrivacyLevel: ‘mask-user-input’, // デフォルトはユーザー入力をマスク
// プライバシーを強制適用するカスタムルールの注入(rrweb互換)
beforeSend: (activity) => {
// 例: 特定のカスタムイベントやURLクエリパラメータのサニタイズ
if (activity.type === ‘view’) {
const url = new URL(activity.view.url);
// URLパラメータに含まれるトークン等を強制的に置換
if (url.searchParams.has(‘auth_token’)) {
url.searchParams.set(‘auth_token’, ‘[REDACTED]’);
activity.view.url = url.toString();
}
}
return true;
},
});
datadogRum.startSessionReplayRecording();
—
4. CSSセレクターと属性を活用した特定要素の非表示化・マスク
Datadog Session Replayでは、HTML要素に特定のクラス名や属性を付与することで、エンジン側に追加の計算コストをかけずに高速なマスク処理を行わせることができる。
① クラス名による制御
- `dd-privacy-mask`: 要素内のテキストをすべてアスタリスク(“)に置換する。
- `dd-privacy-hidden`: 要素自体をDOMから消去されたかのように完全に非表示にする(レイアウトは維持する場合もある)。
- `dd-privacy-allow`: デフォルトでマスクされる領域であっても、安全性が確認されている部分をあえて許可する。
実装例(React / JSX)
import React from ‘react’;
export const UserAccountCard: React.FC<{ user: { name: string; accountNumber: string; email: string } }> = ({ user }) => {
return (
{user.name}
{/ 口座番号は絶対に露出させてはならない:dd-privacy-maskで完全マスク /}
口座番号: {user.accountNumber}
{/ メールアドレスもPⅡ:dd-privacy-maskを適用 /}
{/ 生体認証ステータスなど、機密性の高いグラフィック要素は完全に隠す /}
);
};
—
5. 高度なハック:動的要素・サードパーティウィジェットのカスタムマスク
静的なHTMLであればCSSクラスの付与で完結するが、現代のWebアプリケーションは動的(Dynamic)だ。例えば、Reactの仮想DOMが頻繁に書き換わる環境や、外部の埋め込みチャットウィジェット(Zendesk, Intercomなど)は、予期せぬタイミングでPⅡをレンダリングする。
これらを動的に捕捉し、コードレベルで無力化する高度な手法を解説する。
MutationObserverを活用したリアルタイム・サニタイザー
サードパーティ製ウィジェットが動的にDOMを挿入した場合、CSSクラスを事前に付与することは不可能だ。そのため、クライアントサイドで`MutationObserver`を走らせ、特定の条件に合致する要素へ動的に`dd-privacy-mask`クラスを付与するガーディアン・スクリプトを常駐させる。
/
- 動的に挿入されるサードパーティ製ウィジェットや機密要素を監視し、
- 強制的にDatadogのマスククラスを付与するガーディアンアーキテクチャ
/
export function initPrivacyGuardian() {
const sensitiveSelectors = [
‘input[name=”credit”]’,
‘.third-party-chat-body’, // 外部チャットの本文エリア
‘[data-pii=”true”]’ // 開発者が付与したカスタム属性
];
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as HTMLElement;
// ターゲットセレクターに一致するかチェック
sensitiveSelectors.forEach((selector) => {
if (element.matches && element.matches(selector)) {
element.classList.add(‘dd-privacy-mask’);
}
// 子要素に対しても適用
element.querySelectorAll(selector).forEach((el) => {
el.classList.add(‘dd-privacy-mask’);
});
});
}
});
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
このスクリプトをアプリケーションのエントリーポイント(`index.ts` / `App.tsx`)の最上流で実行することで、未知のDOM変異に対しても数ミリ秒単位でマスクを強制することができる。
—
6. 自動化とCI/CDパイプラインによるガバナンス
コードレビューや人間の目だけに頼るプライバシー保護は必ず破綻する。真のDevOpsエンジニアは、「PⅡ漏洩のリスクを持つコードをデプロイメントパイプラインの段階で物理的に排除する」仕組みを構築する。
静的解析(ESLint)によるマスク漏れ検知
独自のESLintカスタムルール、あるいは既存の正規表現ベースのチェッカー(`eslint-plugin-security`など)を拡張し、機密情報を扱う可能性のあるコンポーネント(例: `CreditCardForm.tsx`)において、親要素に`dd-privacy-mask`が付与されていない場合や、`allow-all-inputs`が宣言されている場合にビルドをエラーにする。
// .eslintrc.js の概念設定例
module.exports = {
rules: {
‘custom/enforce-datadog-masking’: {
// JSX内で敏感なキーワード(password, creditCard, ssn等)を含む
// 入力要素の周辺に dd-privacy-mask が存在することを強制する静的解析ロジック
meta: { type: ‘problem’ },
create(context) {
// AST解析による違反検知コード
return {
JSXAttribute(node) {
if (node.name.name === ‘type’ && node.value.value === ‘password’) {
// パスワードフィールドの検証など
}
}
};
}
}
}
};
—
7. パフォーマンス・オーバーヘッドの極限最適化
Session Replayの有効化は、少なからずCPUおよびメモリに負荷をかける。特にモバイル環境や低スペック端末において、これがUXの低下(フレームレートの低下)を招いては本末転倒だ。
以下のチューニングを施し、オブザーバビリティの「コスト」を最小化せよ。
1. バッチ処理と圧縮の最適化: Datadog SDKは内部でペイロードを圧縮して送信するが、高頻度なDOM変更(例: 無限スクロールやキャンバス描画)がある画面では、`rrweb`のオプションでイベントのサンプリング間隔を調整する。
2. キャンバス(Canvas)の記録除外: `
// ページがバックグラウンドに移行した際にリプレイを一時停止するハック
document.addEventListener(‘visibilitychange’, () => {
if (document.hidden) {
datadogRum.stopSessionReplayRecording();
} else {
datadogRum.startSessionReplayRecording();
}
});
—
結び:オブザーバビリティのプロフェッショナルへ
監視ツールを「導入しただけ」の状態は、リスクを抱えたままアクセルを踏み込んでいるに等しい。Datadog RUMとSession Replayの内部構造を理解し、クライアントサイドのCPU負荷、DOM変異のライフサイクル、そしてプライバシーガバナンスをコードレベルで掌握したとき、はじめて「ノイズのない、真に信頼できるシステム運用」が完成する。
完璧な可視性と、鉄壁のプライバシー。その両立を成し遂げるのは、他の誰でもない、あなた自身のエジニアリングだ。