navigator.plugins 不再是真实插件数据——新版规范已把它写死。空数组、多余条目或与 pdfViewerEnabled 不一致,究竟暴露了什么?
navigator.plugins 曾经是一份真实清单:用户装了哪些 Flash、Java 或 Silverlight 插件,都会原样列给任何一个页面读取。这份真实清单已经不存在了。新版规范把返回的列表直接写死——凡是符合规范的浏览器,现在只会返回同一份固定条目,要么全有,要么全无。听起来这个曾经热门的指纹识别面已经走到尽头了。但并非完全如此:一个本该固定的值,一旦不再固定,或者和另一个描述同一能力的 API 对不上,反而变得有信息量。
核心要点
- 插件列表不再是真实数据。新版规范把
navigator.plugins写死:若支持内嵌 PDF 查看,会返回恰好五个固定条目;若不支持,则返回一个空的PluginArray。 navigator.pdfViewerEnabled才是官方认可的检测方式——MDN 明确说明不要从navigator.plugins推断这一点。- **这份列表依然携带一种检测信号,只是不再关乎身份识别。**一个自称是桌面版 Chrome 的浏览器返回空数组、出现意料之外的多余条目、或者
navigator.plugins与navigator.pdfViewerEnabled结果矛盾,都属于内部一致性破绽,而非正常的浏览器差异。 - 单独来看,
navigator.plugins目前的熵值接近于零——几乎所有真实浏览器都只会落在两种固定状态之一,所以它已经无法像 canvas 或字体指纹那样区分不同访客。它剩下的价值,是叠加在其它信号之上的一致性校验,而不是独立的身份识别手段。 - **这与浏览器扩展是完全不同的检测面。**扩展是用户自行安装的附加组件,靠另一套完全不同的方式检测;
navigator.plugins描述的始终只是浏览器自身内置的插件/PDF 处理层。
navigator.plugins 现在到底返回什么
调用 navigator.plugins 仍然会得到一个 PluginArray——它不是真正的 JavaScript 数组,而是一个具有 length、item(index)、namedItem(name) 的类数组对象。但里面的内容已经不再是从操作系统里探测出来的了。按照当前规范,内容只会是以下两种固定结果之一:
- 若浏览器支持内嵌 PDF 查看,数组会包含五个具体条目:
"PDF Viewer"、"Chrome PDF Viewer"、"Chromium PDF Viewer"、"Microsoft Edge PDF Viewer"、"WebKit built-in PDF"。 - 若不支持,数组为空。
if ("PDF Viewer" in navigator.plugins) {
// 浏览器支持内嵌查看 PDF 文件。
}
仅此而已。不存在第三种状态,不会返回部分列表,一个符合规范的真实浏览器也不可能报出三个插件或叫别的名字的插件。MDN 已把 PluginArray 标记为弃用——属于未来的移除候选——而且它自身的属性在当前浏览器版本中已不再可枚举,这堵住了过去用 for...in 循环遍历数组的老套路。
navigator.mimeTypes 经历了一模一样的变化。它返回一个 MimeTypeArray,按规范在支持内嵌 PDF 查看时包含 application/pdf 与 text/pdf 两个条目,否则返回空列表——写死的方式一样,绑定的也是同一个底层能力位。
navigator.pdfViewerEnabled 才是替代方案——不是 navigator.plugins
正因为插件列表已经收缩为一个关于 PDF 支持的单一是非信号,平台新增了一个直接给出答案的属性:navigator.pdfViewerEnabled,一个纯粹的布尔值。MDN 在 plugins 和 mimeTypes 两个页面都明确写出了这个迁移方向:判断是否支持内嵌查看 PDF 文件,请使用 navigator.pdfViewerEnabled,不要从这两个旧属性去推断。
这条说明正是 navigator.plugins 依然值得深入讨论的原因。想知道"这个浏览器能不能内嵌显示 PDF"的正规代码,现在有了官方认可的直接答案。而仍然靠 navigator.plugins.length 或者按名字查找 "PDF Viewer" 来分支判断的代码,要么是老代码,要么就是在做与 PDF 支持无关的别的事——而这恰恰是检测方最想刻画的那种代码。
为什么一份写死的列表依然是检测信号
一个只有两种合法状态的值本身无法识别访客——但它可以抓出一个不符合自身声称身份的浏览器环境。这就是它剩下的全部用途,而且完全落在内部一致性上,而不是唯一性上:
**该有完整列表的地方出现空数组。**一个自称是现代桌面版 Chrome 的 User-Agent 字符串,按规范应当返回那五条 PDF 查看器列表——因为近期的 Chrome 默认开启内嵌 PDF 查看。在这样一个声称的配置下,navigator.plugins 却是空的,这是一处值得关注的错位,也正是无头和自动化浏览器环境历来容易露出的破绽之一,有时是因为自动化框架直接把 PDF 查看器组件整个禁用了。
**条目与固定集合对不上。**由于规范把五个插件名称写死,任何超出这个集合的插件条目——一个陌生的名字、不同的数量、一个本该是字符串却显示成 [object Object] 的条目——都说明这个属性被脚本修补过,而不是浏览器原生生成的。讽刺的是,那些试图伪造一份"看起来真实"的插件列表的反检测工具,恰恰最容易生成一份与真实、最新浏览器实际输出对不上的列表。而且这种修补通常还会留下第二处痕迹:用普通对象或 JavaScript 数组把这个属性替换掉之后,原生 navigator.plugins 能通过的那几项检查就过不去了——原生对象的原型仍然是 PluginArray,String(navigator.plugins) 仍然是 "[object PluginArray]",它的方法转成字符串后也仍然是原生代码。
**navigator.plugins 与 navigator.pdfViewerEnabled 结果矛盾。**这两个属性描述的是同一个底层能力,只是来自规范的两个不同时期。在真实、未被修改的浏览器里,它们始终一致:非空的插件列表意味着 pdfViewerEnabled === true,反之亦然。一个同时查询这两者、发现它们互相矛盾的页面——比如 pdfViewerEnabled 为真而 navigator.plugins 却是空的,或者反过来——抓到的就是脚本只改了它知道的那个属性——伪造代码常常只盯着自己熟悉的旧属性,忘了还有一个更新的属性存在。
以上这些检测都无法说出访客是谁。它们说明这个环境在内部对不上号——而这正是自动化和伪造浏览器画像比真实浏览器更常留下的破绽。
与浏览器扩展不是同一个面
很容易把"插件"和"扩展"混为一谈,因为两者历史上都算浏览器的附加组件,但检测方式和意义完全不同。navigator.plugins 描述的是浏览器自身内置的插件与 PDF 处理架构,如今已经收缩成那一个写死的 PDF 信号。浏览器扩展(uBlock Origin、密码管理器、广告拦截器)则是用户自行安装的软件,页面根本无法通过这个 API 枚举它们——检测已安装扩展要靠别的技术,比如探测某个扩展自身的可公开访问资源是否有响应。如果你更关心页面能从用户安装的扩展里探到什么,而不是浏览器内置的这个插件标志位,可以看我们关于浏览器扩展隐私风险的文章。
熵值现实检查
有必要直白地说清楚:navigator.plugins 单独来看,如今对指纹的贡献微乎其微。在所有符合规范的浏览器里只有两种合法状态,它几乎不再增加任何区分度——完全无法和 canvas 渲染、已安装字体、WebGL 参数这些信号相比。一些旧的浏览器指纹科普文章依然把 navigator.plugins 描述成一个信息量丰富的逐用户信号,那是在写死改动之前成立的说法;今天再这么看就高估了它的作用。它现在真正有用的地方变窄了、也变了性质:不是"这是谁",而是"这个环境关于 PDF 支持的各项信号,彼此之间、与它自称的身份之间,是否对得上"。这一点和 Permissions API 状态指纹属于同一类——同样是无需弹窗、无需点击的读取,用作一致性校验比用作原始熵值来源更有价值。相比之下,它也远不如 enumerateDevices() 暴露的摄像头、麦克风清单那样,真的能区分不同设备。想了解哪些信号如今依然贡献真实的区分度,可以参考我们的浏览器指纹识别完全指南。
检测你自己的浏览器
用 BrowserInsight 的插件检测工具,你可以直接看到自己浏览器在这一属性上的真实返回值——具体条目、数量,以及 PDF 查看器是否启用。一致性那一面它也一并跑了:插件列表与 navigator.mimeTypes 是否吻合、PluginArray 对象看起来是否仍是原生而非被修补过,再加上接在 navigator.plugins 之后的扩展检测。如果想把这一个属性放进 canvas、WebGL、字体等全部信号的整体画面里看,可以用指纹检测。
常见问题
现代浏览器里 navigator.plugins 为空正常吗?
正常。空数组只是说明浏览器在默认配置下不支持内嵌 PDF 查看——这是一个合法、符合规范的状态,本身并不代表被篡改过。它只有和其它信号放在一起时才有意义,比如某个 User-Agent 自称是理应支持内嵌 PDF 的浏览器版本。
我还能用 navigator.plugins 来判断是否支持 PDF 吗?
技术上可以,但不建议——MDN 明确推荐改用 navigator.pdfViewerEnabled 来做这个判断。navigator.plugins 和 navigator.mimeTypes 都已被列为未来可能移除的候选。
为什么机器人的 navigator.plugins 列表会看起来不对?
因为真实、符合规范的列表只有两种可能的形态,任何试图注入一份"看起来真实"的自定义插件列表来伪装成人类的脚本,本质上都是在对抗一个已经固定的目标:检测方早就知道真列表长什么样,凡是对不上的一眼就能看出来。手工拼出来的插件列表更可能暴露自动化,而不是掩盖它——尤其是当它和 navigator.pdfViewerEnabled 对不上,或者被替换后的对象已经不像一个原生 PluginArray 的时候。
navigator.plugins 对指纹识别还有意义吗?
意义很小,而且和过去完全不同了。它单独贡献的熵值几乎为零。它剩下的价值,是作为 navigator.pdfViewerEnabled 与浏览器声称身份之间的一致性校验,而不是用来区分两个访客。


