天问

如何识别 Chrome 浏览器的匿名窗口

浏览器的隐身窗口可以帮助用户避免 cookie 被窃取、f防止被跟踪记录浏览行为。这篇文章中的一些内容可能与其他系列的浏览器相似或不同,但我只关注基于 Chromium 的浏览器,更具体地说是谷歌的 Chrome 浏览器。

 

在 Chrome 74 版本之前的浏览器中检测隐身窗口

在 Chrome 74 之前,有一个漏洞,很多网站利用这个漏洞来检测用户是否在使用 Chrome 的隐身模式访问网站。网站只需尝试使用 FileSystem API,该 API 用于存储临时或持久文件。该 API 在隐身模式下被禁用,但在非隐身模式下存在,从而产生一个差异,可以用来检测用户是否正在使用隐身模式浏览网站。

在谷歌上随便搜索一下无痕窗口,就会得到很多结果,其中一个是 Stackoverflow 问题,被采纳的回答是:

 

var fs = window.RequestFileSystem || window.webkitRequestFileSystem;if (!fs) {    console.log("check failed?");} else {    fs(window.TEMPORARY,        100,        console.log.bind(console, "not in incognito mode"),        console.log.bind(console, "incognito mode"));}

谷歌在 Chrome 74 中推出了一个新选项(通过#enable- filesystemin -incognito标志访问),阻止了这种检测。他们的解决方案是在隐身模式下使用 RAM 创建一个虚拟文件系统。该保护可以屏蔽上述检测方法,并在随后的稳定版本中默认启用。

如何检测 Chrome 74 版本及之后版本的隐身窗口?

结果表明,上面那种保护是不够的,仍然有可能检测到隐身模式,从而使目前的保护无效。最近,我在摆弄 Quota Management API 时另辟蹊径,使得即使启用了这种保护,也可以检测隐身模式。这个 API 管理分配给浏览器上的应用程序和网站的临时和持久存储的配额。使用以下摘自 Jeff Posnick 文章的代码片段,可以查询临时存储的配额:

 

if ('storage' in navigator && 'estimate' in navigator.storage) {  navigator.storage.estimate().then(({usage, quota}) => {    console.log(`Using ${usage} out of ${quota} bytes.`);  });}

网站/应用程序有两种可用的存储方式,临时存储和持久存储,临时存储可以在不请求任何配额的情况下使用,并且由浏览器上运行的所有网站共享。

关于临时存储及其配额,我通过阅读 Chromium 源代码、文章和 bug 报告收集了一些有趣的观点:(Ref1Ref2Ref3)

 

  • 对于所有应用程序/网站,临时存储的默认配额是可用磁盘的 50%,该空间作为所有应用程序/网站的共享池

  • 应用程序/网站可以通过调用 quota API 的 queryUsageAndQuota() 方法查询它们的配额,而不需要任何权限

  • 隐身窗口的配额是设备内存的一小部分(10%),上限为 120MB

  • 非隐身窗口的配额只是设备存储的一小部分

下表列出了不同磁盘大小设备的最小可用临时存储配额,该配额是根据浏览器试图在设备中始终保持空闲时来计算的。

 

如上图所示,隐身模式和非隐身模式的临时存储配额的关键不同之处在于,在隐身模式下,临时存储配额固定为 120MB,而非隐身窗口则不是这样。从上表可以看出,在非隐身模式下,设备存储小于2.4GB 时临时存储配额才会小于 120MB。但是,就所有实际用途而言,可以安全地假设当前使用的大多数设备都有超过 2.4GB 的存储空间,即在非隐身模式下,临时存储配额一定大于 120MB。

利用这些信息,我想出了一个检测隐身模式的简单规则。如果临时存储配额 <= 120MB,那么可以肯定这是一个隐身窗口。

 

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('Incognito')    } else {        console.log('Not Incognito')    }  } else {  console.log('Can not detect')}
博客地址:http://blog.yoqi.me/?p=16650
扫我捐助哦
喜欢 0

这篇文章还没有评论

发表评论