2019/07/30にChrome76がリリースされました。
76では以前からGoogleが問題視していた「閲覧者がシークレットモードでみているかどうか運営側が分かってしまう」が解決されるはずでした。
しかし、76になってもシークレットモードかどうか判定出来る方法がまだ二つ有る、というニュースが話題になっています。
実際Chrome76でこの二つの方法を用いてシークレットモードを検出できるのかどうか検証しました。
目次
Chrome76以前のシークレットモード判定方法
今までのシークレットモードはブラウザのFileSystemAPIが正常動作しなかった為「ブラウザからハードディスクにアクセスしてエラーになった場合はシークレットモード」と判断出来ていました。
window.webkitRequestFileSystem(
window.TEMPORARY,
100,
console.log.bind(console, "通常モード"), // 正常ハンドラ
console.log.bind(console, "シークレットモード") // 異常ハンドラ
);
Chrome76で入った対策
これに対し適当なChrome76では、シークレットモード時はディスクファイルシステムの替わりにメモリファイルシステムが提供されるようになりました。
これによってシークレットモードでもこのAPIは正常ハンドラに入るようになり、シークレットモードかどうか分からなくなります。
「ディスクファイルシステムの替わりにメモリファイルシステムを用意してFileSystemAPIが正常に動くようにする」がGoogle側の対策でした。
この対策に二つの抜け道が発見されたのが今回のニュースの内容です。
検証1:ファイルシステムのディスク容量で判断する
vikas mishraさんが発見したQuota Management APIを使い、ブラウザが使えるディスク容量を調べる方法。
Chromeが使えるHDD容量の仕様は概ねこんな感じです。
- HDD総容量の10分の1(ただしMAXは2GByte)
- テンポラリディスクの容量はその50%になる
- 仮想的なメモリファイルシステムだと総容量が小さく2.4Gbyte以下
- 2.4Gbyte以下の総容量だとテンポラリディスク容量は120Mbyte以下になる
今時2.4GByteのハードディスク容量しかない端末は少ないので、クォータが120Mbyte以下だったらメモリファイルシステム = シークレットモードである、と判断出来るとのこと。
試しにindex.htmlを作り、適当なWebサーバに載せて検証してみます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script>
// Chrome76以前のシークレットモードの見分け方
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
console.log("check failed?");
} else {
fs(
window.TEMPORARY,
100,
console.log.bind(console, "旧調査方法の判定:通常モード"),
console.log.bind(console, "旧調査方法の判定:シークレットモード"
)
);
}
// Chrome76でも見分けられるmishraさんの方法
async function start() {
if ("storage" in navigator && "estimate" in navigator.storage) {
const { usage, quota } = await navigator.storage.estimate();
console.log(`Using ${usage} out of ${quota} bytes.`);
if (quota < 120000000) {
console.log("新調査方法の判定:シークレットモード");
} else {
console.log("新調査方法の判定:通常モード");
}
} else {
console.log("Can not detect");
}
}
start();
</script>
</head>
<body></body>
</html>
ChromeからCtrl + Shift + Nでシークレットモードタブを作り、上記index.htmlにアクセスした実行結果。
Using 0 out of 110763386 bytes.
新調査方法の判定:シークレットモード
旧調査方法の判定:通常モード
確かに120Mbyte以下になっていて、旧調査方法では見抜けなくなったシークレットモードが見抜けました。
2.4GbyteしかHDDを積んでない端末のChromeの場合は判断を誤るでしょうが、確度の高い見分け方だと言えそうです。
検証2:ディスク書き込み速度で判断する
続いてJesse Liさんが見つけた「メモリファイルシステムはディスクファイルシステムよりアクセスが速い」事実を利用した検出方法。
ファイル書き込みを100回繰り返し、処理が返ってきた時間を計るベンチマーキング方式になっています。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script>
const largeStrings = [
// These strings are 5000 characters long. I generated them by running
// base64 /dev/urandom -w 0 | head -c 5000
"odE141SCRsNhfNBb95VhqRubp+fXTF1Dricc0G9wWrQcXRvu3uhGRh4t2TiUZF1BdSKLOrnG...",
"pdfhLvvnkBGjbuR1/0WcCcM2li/cYOQ/wZGPAofjBXxo6PvhoEAWYtEMtTlbcLm+dPxwQFm8...",
"Xfo5aKCHnIQc9zMtUWmGYiwzBJuDQLEVyg0t9ID2ZsCVMnVD7h8juo9Bmd+e2VdmofvGkFoa...",
"jsYalJDnye4x5Vvl9w+F7aRrVx+WcJT5E7rzB9UNxb7iyY+mFAvsllN95ZDom50+GhhBuT+l...",
"QcaZ/f91np7UkMvy4jrJks5Iogpgik0JZA0kCeXEPc2vdFYHKKIVT+nKmrva0qUee14LXh9Y..."
];
const SIZE = 6 * 1024 * 1024; // 6 MB
// Completely arbitrary numbers. Probably make them as high as you can tolerate:
const NUM_BENCHMARK_ITERATIONS = 200;
const NUM_MEASUREMENTS = 100;
const writeToFile = (fs, data) => {
return new Promise(resolve => {
fs.root.getFile("data", { create: true }, fileEntry => {
fileEntry.createWriter(fileWriter => {
fileWriter.onwriteend = resolve;
var blob = new Blob([data], { type: "text/plain" });
fileWriter.write(blob);
});
});
});
};
const runBenchmark = async fs => {
const time = new Date();
for (let i = 0; i < NUM_BENCHMARK_ITERATIONS; i++) {
for (let j = 0; j < largeStrings.length; j++) {
await writeToFile(fs, largeStrings[j]);
}
}
return new Date() - time;
};
const onInitFs = async fs => {
const timings = [];
for (let i = 0; i < NUM_MEASUREMENTS; i++) {
timings.push(await runBenchmark(fs));
}
console.log(timings);
};
window.webkitRequestFileSystem(window.TEMPORARY, SIZE, onInitFs);
</script>
</head>
<body></body>
</html>
通常モード(ディスクファイルシステム)の実行結果。
[2435, 2136, 2154, 2198, 2147, 2122, 2124, 2091, 2274, 2067, 2202, 2240, 2334, 2218, 2129, 2154, 2122, 2233, 2232, 2140, 2131, 2192, 2243, 2214, 2140, 2200, 2253, 2353, 2294,
2216, 2137, 2066, 2112, 2101, 2118, 2035, 2173, 2285, 2117, 2167, 2091, 2137, 2077, 2200, 2219, 2108, 2128, 2110, 2104, 2304, 2186, 2781, 3418, 2757, 2546, 2356, 2391, 2443, 2366,
2391, 2301, 2405, 2380, 2375, 2511, 2578, 2410, 2450, 2245, 2387, 2357, 2349, 2293, 2391, 2326, 2289, 2347, 2416, 2322, 2470, 2249, 2281, 2335, 2222, 2320, 2360, 2252, 2350, 2264, 2315,
2402, 2452, 2403, 2390, 2336, 2305, 2409, 2340, 2396, 2326]
プライベートモード(メモリファイルシステム)の実行結果。
[1094, 997, 998, 994, 1010, 1039, 1063, 1171, 1044, 1016, 984, 981, 984, 1004, 972, 983, 991, 1017, 982, 987, 982, 996, 1284, 996, 978, 992, 1015, 1157, 969, 981, 1008, 976,
985, 989, 997, 1008, 989, 1196, 974, 1040, 973, 981, 1102, 996, 987, 995, 972, 1019, 994, 987, 982, 1013, 1214, 980, 987, 982, 1033, 980, 1144, 984, 1036, 983, 985, 995, 977, 1023,
977, 1001, 1210, 1004, 984, 985, 982, 1026, 1147, 977, 991, 982, 1017, 993, 1139, 981, 1046, 980, 983, 982, 1019, 988, 998, 977, 1197, 983, 985, 982, 990, 1010, 985, 1196, 1040, 1048]
明らかにプライベートモードの方が速いですね。
めちゃくちゃ速いHDDだったらどうするのかとか、ベンチマークが終わるまで結構時間が掛かるとか懸念は残りますが判断材料には出来そうです。
今後も改善は続くとのこと
どちらもかなり信憑性のある見分け方でした。
冒頭のニュースサイトがGoogle側に問い合わせたところ、今後改善していく返答があったようです。
ただ改善するにしてもChromeが使えるメモリを増やすのはナンセンスですし、RAMがHDDより速いことは事実です。
どういった対応がなされるのか、ウォッチする側としてはGoogleの出方が楽しみになって来ました。