概要
ブラウザのプライベートモード(またはシークレットモード、InPrivateブラウジングなど)を識別したい。しかしながら、JavaScriptによる確実な識別は困難である。
結論として、執筆時点ではJavaScriptを利用して全ブラウザ・バージョンにわたりプライベートモードを100%判定する方法は存在しない。ある程度は判定可能だが、それも限定条件下での回避策か確率的推測に過ぎない。
ブラウザのプライベートモード(またはシークレットモード、InPrivateブラウジングなど)を識別したい。しかしながら、JavaScriptによる確実な識別は困難である。
結論として、執筆時点ではJavaScriptを利用して全ブラウザ・バージョンにわたりプライベートモードを100%判定する方法は存在しない。ある程度は判定可能だが、それも限定条件下での回避策か確率的推測に過ぎない。
判定が不可能な主な要因は、ブラウザベンダーがプライベートモードを示す標準APIが提供されていない点にある。
これはプライベートモードが「追跡されたくない」といったユーザーのプライバシー保護を重視するためと考えられる。容易な検知はプライバシー保護の目的を損ないフィンガープリント(fingerprint)に悪用されるリスクもあるためか、明確な判定手段は提供されていない。
標準APIがないため、いわゆるハックを利用した手法(非公式な回避策や挙動の利用)による検出がプライベートモードを判定するために試みられている。
これらの手法は、プライベートモードと通常モードで特定のWeb APIの挙動の差異に基づいたものである。
プライベートモードは、プライバシー保護の観点からデータ永続化を避けるため一部API機能を制限することがあるらしい。この「制限」や「通常と異なる挙動」を検知し、プライベートモードの可能性を推測する。
主に以下のAPI群の挙動差異を判定に利用する。
(localStorage, sessionStorage, IndexedDB)
:localStorage
/sessionStorage
: データ書き込み (setItem
) 時に、十分なストレージ容量があってもQuotaExceededError
等のエラーが意図的に発生させられることがある。IndexedDB
: 永続的なデータベース接続の確立が許可されず、indexedDB.open()
リクエストが失敗し、onerror
イベントハンドラが呼び出されるケースもある。(navigator.storage.estimate())
:navigator.storage.estimate()
メソッドで推定値を取得できる。quota
プロパティ) が、通常モードと比較して極端に小さい値(例えば0バイト)として確認できる場合があり、これがプライベートモードの指標とされる。FileSystem API
(現在は非推奨)などが、プライベートモードでの利用制限を根拠に検出手段として利用されたことがある。これらの挙動差は副作用を利用した判定に過ぎない。例えばlocalStorage
のエラーは、ディスク逼迫でも起こり得るため、複数APIの挙動を組み合わせ、「プライベートモードである蓋然性が高い」と推測するのがハックを利用した際の基本アプローチになる。
以下はあくまでサンプルコードであり、ブラウザ間の差異やエラー処理などは未考慮のため、実運用への適用は推奨しない。
/** プライベートモードの可能性を推測する */
async function guessIfPrivateMode() {
let privateIndicators = 0; // 指標カウンター
const checks = { localStorage: 'unknown', indexedDB: 'unknown' }; // チェック結果
// 1. localStorage アクセス試行
try {
localStorage.setItem('__test_private_mode__', '1');
localStorage.removeItem('__test_private_mode__');
checks.localStorage = 'writable';
} catch (e) {
privateIndicators++;
checks.localStorage = 'error';
console.warn('localStorage access failed:', e);
}
// 2. IndexedDB アクセス試行 (非同期)
await new Promise((resolve) => {
try {
const request = indexedDB.open('__test_private_mode__');
request.onerror = (event) => {
privateIndicators++;
checks.indexedDB = 'error';
console.warn('IndexedDB open failed:', event.target.error);
resolve();
};
request.onsuccess = (event) => {
try {
event.target.result.close();
indexedDB.deleteDatabase('__test_private_mode__');
} catch (closeErr) {
/* ignore */
}
checks.indexedDB = 'success';
resolve();
};
setTimeout(() => {
// Timeout
if (checks.indexedDB === 'unknown') {
checks.indexedDB = 'timeout';
resolve();
}
}, 1000);
} catch (e) {
privateIndicators++;
checks.indexedDB = 'exception';
console.warn('IndexedDB access threw exception:', e);
resolve();
}
});
// 3. 他のチェックも追加可能...
// 結果判定
const isPotentiallyPrivate = privateIndicators > 0;
const confidence = privateIndicators / Object.keys(checks).length;
console.log('Detection checks:', checks);
console.log(`Private indicators: ${privateIndicators}`);
return {
isPotentiallyPrivate,
confidence: parseFloat(confidence.toFixed(2)),
checks,
};
}
// 実行例
guessIfPrivateMode().then((result) => {
console.log(`Is potentially private? : ${result.isPotentiallyPrivate}`);
console.log(`Confidence score (simple): ${result.confidence}`);
});
このようなハックの実用性には限界がある。
ハック的手法が依存するAPIの挙動(副作用)は、Web標準として保証されたものではなく、あくまで特定バージョンのブラウザにおける実装の詳細に過ぎない。
そのため、ブラウザがセキュリティパッチ、新機能追加、仕様変更などでアップデートされるたびに、これらの副作用は変更・修正される可能性がある。例えば、以前はエラーを返していたストレージ操作がエラーを返さなくなったり、逆の変更が生じたりする可能性がある。
さらに、ブラウザベンダーがプライバシー保護強化の一環として、これらの検出手法を意図的に無効化する(例:プライベートモードでも通常モードと同じ挙動に見せる)可能性も考えられる。
結果として、一度確立したかに見えた検出ロジックも、ブラウザのアップデートによって有効性を失うリスクが高い。
プライベートモードの実装方法は、ブラウザ(Chrome, Firefox, Safari, Edge等)や動作するOS(Windows, macOS, Android, iOS等)によって差異が存在する。例えば、iOS上のSafariにおけるプライベートモードのストレージ制限は、デスクトップ版Chromeのシークレットモードとは異なる挙動である。
環境による仕様差があるため、一貫して動作する検出ロジックを作成することは困難である。
これらのハック的手法は、プライベートモードであることを示す確実な証拠ではなく、あくまで状況証拠に基づいているため、本質的に判定精度には限界があり、誤検知のリスクがある。
具体的なリスクのひとつが偽陽性(False Positive)である。これは、実際には通常モードであるにも関わらず、プライベートモードであると誤って判定してしまうケースを指す。例えば、ユーザーのディスク容量が本当に不足していてストレージAPIがエラーを返した場合や、特定のブラウザ拡張機能がストレージAPIの挙動に干渉した場合などに発生し得る。
もうひとつのリスクが偽陰性(False Negative)である。これは、実際にはプライベートモードであるにも関わらず、通常モードであると誤判定(検出に失敗)してしまうケースを指す。例えば、ブラウザの実装変更により、プライベートモード固有の挙動(副作用)が隠蔽され、検出できなくなった場合などに起こり得る。
これらの誤検知は、アプリケーションがユーザーのモードに基づいて不適切な動作(例:プライベートモードのユーザーに不要な警告を表示する、通常モードのユーザーにプライベートモード限定機能を提供してしまう)を引き起こす原因となる。
JavaScriptを用いたフロントエンドでのプライベートモード判定は、ハック的手法の技術的限界により、確実な実現は困難であり、実運用上のリスクが高い。