返回知识中心
指纹

CreepJS 预测报告:聚合逻辑与未知状态

对比 CreepJS 的 getPrediction 查找与 renderPrediction 信号聚合、unknown 状态;两者都不能证明设备身份。

BotBrowser Team

文档中心

想直接进入 指纹 文档吗?

这篇文章属于知识中心的指南库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

BotBrowser 可以让声明的浏览器配置在浏览器可见表面之间保持一致,但不能认证第三方的群体预测结果。在固定版本的 CreepJS 源码中,getPrediction() 查找群体数据行并选择候选值,而 renderPrediction() 则独立读取 decryptionData、聚合信号字段并显示 review 或 score 状态。这是两条不同的源码路径;任一路径的输出都不能证明个人身份、机器身份或 bot 判定。

TL;DR

  • getPrediction 会在传入数据中查找 id 与请求 hash 相等的行,也接受把两边转换成字符串后相等。名为 decrypted 的局部变量保存的是匹配到的对象键;这个函数没有解密数据行。
  • 系统、GPU 和 GPU 品牌字段只有在候选数组恰好有一个值时才返回。设备字段还可以在多个名称共享一个基础名称时返回该名称。
  • 渲染器把 18 个具名信号对象中的值聚合到三个 JavaScript Set 对象,分别保存 GPU 品牌、GPU 和设备。Set 去重,代码随后删除 undefined;聚合结果不再保留每个值由哪个信号对象提供。
  • unknownHTML、pendingReview、群体混合评分、预测标记和错误占位符属于不同代码分支。它们都不是标签正确率的校准概率。

Contents

CreepJS 的两条路径:getPrediction 查找 hash 并选择候选;renderPrediction 独立聚合信号并显示状态
固定源码把查找、候选缩减、聚合和界面渲染分成不同步骤。

预测标签是聚合结果

“预测”一词容易让人作出超出实现能力的推断。读者可能看到设备名称旁边有分数,就认为页面识别了某台实体设备,或测量了该名称正确的概率。固定版本的 CreepJS 实现并不支持这些结论。源码展示了它如何从输入中选择并呈现特定值,却没有提供校准研究、置信区间或个人级标识符。

两个函数回答的是不同问题。getPrediction 查找一条群体数据行,并从四个候选列表中产生 system、device、gpu 和 gpuBrand 字段。renderPrediction 接收信号对象,把选定字段跨对象组合,再构建预测区域和单独信号行的界面标记。查找结果和信号渲染摘要不应被压成一个不透明分数。

CreepJS 信号组解读文章介绍如何阅读各表面的标签,信号组交叉检查分析则梳理 botHash 背后的有序模式。这两篇都没有追踪群体数据行匹配,也没有覆盖预测面板的候选选择与显示分支。

当报告变化时,这一区别很重要。标签可能消失,因为候选列表现在有两个值、信号值不再共享一个基础名称、某个字段没有提供,或者预测进入了错误分支。这些路径含义不同。没有一个唯一值不代表浏览器没有 GPU;错误占位符也不证明所有信号模块都运行后产生了分歧。

源码范围固定为 CreepJS src/prediction/index.ts 的提交 10aa6724cd33a1015db1574211890518cd04f0cc,不是对当前在线 CreepJS 服务的观察。更广泛的 指纹与隐私威胁文档 介绍浏览器指纹信号,但没有逐分支说明这个预测模块。

先匹配群体数据行,再读字段

getPrediction 接收 hash 和 data。它遍历 data 的键,在每个键所对应的数组中搜索,并将元素的 id 与请求的 hash 比较。比较既接受直接相等,也接受两边转换成字符串后相等。匹配元素所在的键被存进名为 decrypted 的局部变量。

这个变量名本身不能证明代码执行了密码学操作。在此函数中,相关操作是数组搜索和 ID 比较。源码没有在这里解密数据行。准确说法是“按 hash 查找群体数据行”,不是“解密预测”。局部变量的名字不会改变代码实际执行的操作。

找到匹配元素后,函数把四个数组复制到局部变量:devices、systems、gpus 和 gpuBrands。缺失数组会变成空数组。随后,返回对象从每个候选列表中选择一个可见值。因此,找到数据行时,一个或多个返回字段仍可能是 undefined:匹配数据行与取得唯一值是两个不同步骤。

源字段查找操作保守解读
item.id与传入的 hash 比较,也接受字符串化后的相等在给定数据对象中找到了匹配记录。
item.systems缺失时用空列表;只有一个候选时才返回系统零个或多个候选都不会产生唯一 system 值。
item.gpus缺失时用空列表;只有一个候选时才返回 GPU没有返回 GPU 标签不等于浏览器没有 GPU。
item.gpuBrands缺失时用空列表;只有一个候选时才返回品牌函数不会给多个品牌排序,也不会报告其中一个的置信度。
item.devices缺失时用空列表;返回单个候选或共同基础名称文本前缀相同是选择规则,不是独立硬件验证。

最后一行尤其容易被过度解读。代码注释用 Android 10 说明它如何成为 Android 10 和 Android 10 Blah Blah 共同的基础名称。这只是字符串包含关系的例子,不表示 CreepJS 在真实浏览器中确认了具体操作系统版本。

候选数量决定哪些值会返回

返回规则有意采用不对称设计。三个字段要求恰好一个候选,而 device 还有共享基础名称的后备规则。数组长度解释了为什么群体数据行存在时,界面字段仍可能缺失。

字段固定源码中的候选条件条件不满足时的返回值
systemsystems.length == 1undefined
gpugpus.length == 1undefined
gpuBrandgpuBrands.length == 1undefined
device一个设备候选,或一个名称被每个候选字符串包含如果没有名称被所有候选共享,基础名称辅助函数返回 undefined。

对于系统、GPU 型号和 GPU 品牌,实现不会挑选最常见的候选,也不会按分数排序。它只检查数组是否有一个元素;否则字段就是 undefined。这是明确的歧义边界,不是隐藏的排序算法。

对于设备,getBaseDeviceName 会寻找候选字符串 a,要求每个候选字符串 b 都包含 a。只有一个候选时,代码会直接返回它,不调用辅助函数。有多个候选时,可能返回共同基础名称;如果没有共同名称出现在所有候选中,这个分支就不会给出一个设备名称。此规则基于文本,不应描述成硬件距离计算。

以下源码推导的例子展示代码路径,不是在线报告:

候选列表代码路径结果结果不能证明什么
systems 有一个条目该条目作为 system 返回标签识别了某个人的操作系统。
gpus 有两个条目不返回唯一的 gpu 值两个 GPU 都没有被任何信号模块观察到。
devices 包含 Android 10 和 Android 10 Blah Blah可能返回共同基础名称 Android 10某个具体 Android 版本经过了独立验证。
devices 有互不相关的名称,且不存在所有候选共享的基础名称基础名称辅助函数不返回值浏览器无效或正在自动化运行。

undefined 是值得保留的状态。将它替换成第一个候选、多数猜测或推断出的设备,会凭空添加函数没有实现的选择规则。比较记录应把“没有返回唯一值”与“源字段不存在”分开记录。

18 条记录进入三个去重集合

聚合字段 gpuSystem 和 gpuModelSystem 不描述 WebGL context 的创建或扩展支持;CreepJS WebGL 报告分析会单独检查渲染器的 context 与能力信号。

renderPrediction 读取具名信号对象,并创建三个 Set 实例。每个集合都从 18 条记录中读取一个属性:jsRuntime、emojiSystem、domRectSystem、svgSystem、mimeTypesSystem、audioSystem、canvasSystem、canvasPaintSystem、canvasTextSystem、canvasEmojiSystem、textMetricsSystem、webglSystem、gpuSystem、gpuModelSystem、fontsSystem、voicesSystem、screenSystem 和 deviceOfTimezone。

三个属性分别投影到不同集合:一个集合收集 gpuBrand,另一个收集 gpu,第三个收集 device。读取属性前,每条记录都会用空对象作后备。集合构建后,代码删除 undefined。相同值会折叠成一个,因为 Set 只保留唯一值。

聚合集合从 18 条记录读取的属性源码中直接可见的转换
gpuBrands.gpuBrand构造集合,然后删除 undefined。
gpus.gpu构造集合,然后删除 undefined。
devices.device构造集合,然后删除 undefined。

该操作将一组具名字段缩减为唯一值,但结果集合不会指出每个值来自哪个信号对象。如果五个模块提供同一个 gpuBrand,集合中只有一个值,不会留下“5 次”的计数。如果两个模块不一致,集合中可能有两个值,但界面仍不显示各自来自哪个 API。上游模块在代码中列出了来源;如果比较需要追溯来源,就要将来源另存到单独记录中。

这个集合也不同于群体数据行的候选列表。getPrediction 的候选数组用于判断某个 hash 是否能返回唯一值。renderPrediction 的信号集合则把各字段值组合成可见摘要。用“共识”一个词描述两条路径,会掩盖这个差异。

18 个来源字段可以按命名表面分组,但这些名称不意味着质量相等或彼此独立。Canvas 图像、绘制、文本和 emoji 在源码中是不同输入;gpuSystem 和 gpuModelSystem 也彼此独立。把它们视为完全独立的投票,会引入实现中不存在的统计假设。

分别看设备选择与评分状态

界面渲染设备标签前,会先计算一个特殊的 Windows Remote Firefox Privacy 选择。源码统计包含“Windows”的设备字符串数量,再除以全部设备字符串数。只有比例大于 0.5 时才进入这条分支,然后按固定偏好顺序查找:Windows 7、Windows 8、Windows 8.1、Windows 11,最后 Windows 10;每个版本都先检查 64 位写法,再检查较短写法。如果比例不超过一半,辅助函数返回 undefined。

最终的 deviceName 是 RFP 辅助函数结果;若结果为 falsy,则使用设备名称的共同基础部分。注释说样本数据会忽略 Firefox RFP,因为它返回 Windows 10;随后说明多个 Windows 版本同时出现时采用的优先顺序。这是该源码特有的显示规则,不证明一般人群遵循同一顺序,也不应被描述为普遍准确的操作系统检测器。

crowdBlendingScoreGrade 把数值区间映射到字母:90 及以上为 A,80 及以上为 B,70 及以上为 C,60 及以上为 D,低于 60 为 F。只有 crowdBlendingScore 的 JavaScript 类型为 number 时,界面才显示百分比和字母。单独的 bot 分支显示 locked。代码定义了字母映射和界面条件,却没有说明分数预测什么,也没有说明它如何校准。

当 pendingReview 为 truthy 时,模块会把它显示为独立提示。它应与评分和预测名称分开记录。没有另一份来源明确定义状态时,固定源码不支持把它改述为“失败”“已批准”或“人工审核已完成”。

可以记录以下界面状态,但不要把它们变成置信度声明:

可见状态源码条件有边界的解读
合并标签前的 **deviceName 和 gpuBrandName 都是 truthy渲染器有两个显示字符串。星号不是概率。
单一标签前的 *两个显示字符串中只有一个是 truthy此分支只有其中一个字符串可显示。
空白预测图标两个字符串都不是 truthy此分支不会显示合并标签。
pending review: …pendingReview 是 truthy界面显示传入字段;固定源码没有定义审核结果。
群体混合分数及字母分数类型是 number,且没有进入锁定分支界面显示传入的数字和分档;准确度未知。
lockedbot 分支为 truthy界面进入锁定分支,此处不展示评分。

hasValue 辅助函数还需要额外注意。它查找第一个类型不是 undefined 的属性值;调用它的代码会对结果取反,以决定是否显示 unknownHTML。已定义的 false 或 0 本身也是 falsy,因此也可能进入未知状态的显示分支。这意味着 unknown 标签不能证明信号模块没有执行,它也可能反映了辅助函数处理 falsy 值的方式。这个观察只适用于固定实现;后续版本改动该函数后应重新核对。

predictionErrorPatch 是另一条分支。传入错误且预测元素存在时,它会渲染 Prediction Failed,并在许多行显示空白图标。这个占位符描述错误界面,不是候选数量规则,也不是一次成功运行中所有信号都缺失。记录时应把错误分支与正常报告中单独显示为未知的行分开。

保留绑定来源的比较记录

两个报告不同时,先记录问题和源码版本,再解释标签。以下字段能保留实现中的差异,同时不复制原始 hash,也不收集完整浏览器标识:

记录字段保留内容原因
来源仓库、commit SHA 和文件路径后续版本可能改变查找或渲染行为。
查找状态传入数据中是否有 hash 匹配行;不要保存 hash 本身行是否存在与字段是否唯一是不同结果。
候选数量system、设备、GPU 和 GPU 品牌的计数返回规则依赖数组长度和共同设备基础名称。
聚合状态哪些具名信号字段存在,以及每个集合有零个、一个还是多个唯一值去重集合不会保留原始贡献列表。
渲染状态预测标记、unknown 分支、pendingReview、数字评分、锁定分支或错误占位符看似相同的空白区域可能来自不同路径。
限制记录是固定源码分析、当前页面观察,还是其他明确限定的来源源码本身不能证明服务当前输出或准确度。

不要只因为查找使用 hash 就保存传入的 hash。通常用布尔值记录“是否存在匹配行”,再记录候选数量,就足以回答问题。保存原始标识符会增加留存数据,却不会改善对代码分支的解释。

这份表用于解释和比较公开来源的行为。它不是复现 CreepJS 后端的步骤,不是 CreepJS 准确率基准,也不是改变第三方评分的方法。要支持这些结论,需要本源码文件没有提供的其他证据。

Sources

Practical conclusion

应把 CreepJS 预测面板看成一组依赖源码分支的输出。先确认群体数据中是否有匹配行,再分别记录候选数量、信号对象唯一值和界面状态。单一返回值、共同设备基础名称、评分分档、待审核提示和 unknown 标记不能互相替代。固定源码解释的是标签如何被组装,不会因此成为经过验证的设备身份或校准过的 bot 判断。

#CreepJS#Browser Fingerprinting#隐私

本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。