CreepJS 不只看单个伪造值,而是交叉核对可信与不可信来源,揪出难以掩盖的矛盾。
大多数指纹识别工具只回答一个问题:这个浏览器有多独特? CreepJS 是一个开源指纹研究项目,它问的是另一个问题:这个浏览器说的是真话吗? 它不会只是采集一个 Canvas 哈希或一个 WebGL 渲染器字符串就了事,而是把每一个信号都与该浏览器暴露出的其他所有信号进行交叉核对——并与该信号在真实实现中本应表现的行为进行比对。当某个值单看似乎合理,却与浏览器透露的其他信息相矛盾时,CreepJS 就把它称为一个谎言,其信任分数也会相应下降。本文将以通俗的方式讲解这套测谎模型:「可信」与「不可信」来源分别指什么、篡改原型或 getter 为何会露出马脚,以及为什么反检测浏览器和粗糙的 user-agent 伪装都会栽在同一类检查上。
核心要点
- CreepJS 的核心理念是一致性,而非身份:它不需要一个记录「正确」值的数据库,只需要证明浏览器自身的信号相互矛盾,或者与真实实现的行为方式不符。
- 它会比较同一事实的可信来源与不可信来源——直接读取一个属性 vs. 间接推导出同一个值,或者比较主线程结果与在 Worker 中运行同一计算的结果——并标记出两者之间的分歧。
- 原型和 getter 篡改——也就是伪造工具用来覆盖
navigator.userAgent或HTMLCanvasElement.prototype.toDataURL等值的标准手法——会留下结构性证据:被改动的属性描述符、不再匹配原生代码的.toString()输出,以及跨调用会发生变化的引用身份。 - 用于拦截属性访问的 Proxy 陷阱本身也是可被检测的,因为真正的浏览器 API 回答一次查询根本不需要
Proxy包装器。 - 正是同一套测谎逻辑,使得粗糙的 user-agent 或 Canvas 伪装——乃至构建精良的反检测浏览器——往往会留下一整套矛盾的模式,而不只是单一的明显破绽。
CreepJS 实际检测什么
根据其 GitHub 仓库的说明,CreepJS 的既定目标是「揭示现代反指纹扩展和浏览器中的弱点与隐私泄漏」。它运行一整套广泛的检查——Canvas 与 WebGL 渲染、音频与字体可用性、CSS 计算样式、屏幕与时区数据、JavaScript 运行时特征等等——而不是只把结果哈希成一个单一标识符,它会逐项评估每个信号的内部合理性。输出结果是一个信任分数,外加一份逐项细分:哪些信号看起来是原生的,哪些看起来是拼凑、打过补丁,或者以其他方式与真实浏览器实例不一致。
这个视角对任何审计自身设置的人都很重要。指纹唯一性工具告诉你,你有多容易被识别。而测谎工具告诉你,你所呈现的身份是否真的能自圆其说——这更接近于 Bot 缓解、欺诈评分和反检测浏览器检测系统在生产环境中实际检查的内容,详见网站如何检测反检测浏览器和指纹伪造。
测谎模型:一致性,而非身份
一个粗糙的指纹检查只是读取一个值并记录下来。而测谎检查会通过不止一条路径去读取同一个事实,再比较两者的答案。如果一个浏览器真的如它所声称的那样,那么通向同一事实的每一条路径都应该给出一致的答案,因为它们最终都源自同一个引擎、同一套渲染管线和同一个操作系统。如果某个东西被打过补丁以呈现虚假的值,要让每一条路径都一致地呈现补丁后的结果是非常困难的——而那些被遗漏的路径,正是谎言暴露的地方。
这与指纹一致性检查背后的原理是一样的:唯一性衡量的是一个指纹有多罕见,而连贯性衡量的是它是否可能合理地属于同一台真实设备。CreepJS 把这种连贯性理念应用到了更细的粒度上——不只是「GPU 是否与所声称的操作系统匹配」,而是「这个特定 API 的底层行为,是否与该 API 真实实现所产生的行为相符」,并将这种比对在数十项独立检查中重复进行。
可信来源与不可信来源:谎言在哪里被抓住
理解这类工具的一个有用视角,是把同一个底层事实的来源区分为可信与不可信两类。可信来源是指伪造脚本难以拦截或代价高昂的来源——从不同的执行上下文中读取的值、作为无关代码副作用而产生的计算结果,或者从行为中推导出的值,而不是脚本可以直接重新赋值的属性。不可信来源则是那些显而易见、经常被伪造的属性本身——navigator.userAgent、Canvas 的 toDataURL() 调用、某个 WebGL 参数——这些都是每一款反检测工具和隐私扩展早已知道要去拦截的目标。
这类检查问的不是孤立地「这个不可信值是不是真的」,而是「这个不可信值是否与可信值相符」。以下是这种模式的几个常见例子:
- 主线程 vs. Worker 上下文。 同一个计算——一次 Canvas 绘制、一次时区读取、一次数学运算——既可以在主线程上运行,也可以在 Web Worker 内运行。真实浏览器在两处会得到相同的结果,因为两个执行上下文共享同一个底层引擎。伪造工具经常只给主线程的全局对象打补丁,而漏掉了 Worker 独立的全局作用域,于是两个上下文的结果就对不上了。跨上下文 Canvas 一致性检查——比较 Worker 中
OffscreenCanvas的输出与文档内 canvas 的输出——就是这种通用模式的一个具体实例。 - 直接读取 vs. 推导值。 有些事实既可以直接从一个属性读取,也同样可以间接推导出来——通过某个副作用、一条错误消息,或者只有真实实现才能正确产生的某种行为。如果直接读取的值和推导出的值不一致,那么其中只有一个是真的——而且几乎从来不会是那个容易被打补丁的值。
- 声称的身份 vs. 可观察的行为。 user-agent 字符串声称了一种浏览器和引擎;而 JavaScript 引擎的计时、错误消息的措辞以及功能存在与否,无论字符串怎么说,都会揭示真正在运行的是哪种引擎——这正是如何检测 User-Agent 伪装一文所解释的确切机制。
以上这些检查都不需要 CreepJS 知道你「正确」的指纹应该是什么。它只需要两个能观察到同一事实的独立窗口,以及一个信念:真实浏览器不可能不通过这两个窗口给出同样的答案。
属性描述符、Getter 与 Proxy 陷阱
除了跨来源比较之外,CreepJS 的检测方法还包括直接的原型篡改检测——寻找浏览器 API 被覆盖过的结构性证据,而不只是检查其返回值是否看起来合理。
伪造 navigator.userAgent 或某个 Canvas 方法这类值,通常意味着重新定义一个 getter 或重新赋值一个原型方法。这种干预所改变的不只是值本身:
- 属性描述符。 对一个原生方法调用
Object.getOwnPropertyDescriptor(),会返回特定的、可预期的结构,其.toString()结果为"function name() { [native code] }"。而被粗糙打过补丁的版本往往会返回一个可见的函数体,或者一个忘记合成原生代码标记的覆盖版本。 - Proxy 陷阱。 用
Proxy拦截属性访问是动态打补丁 getter 的常见手法,但一个活动的 Proxy 在引用相等性检查下的表现与原生属性不同——两次访问可能返回可区分的包装器实例,或者它的get/has陷阱会响应一些原生属性根本不需要回答的探测。 - 引用身份。 一个真正的原型方法,每次被访问时都是完全同一个函数对象。而实现为新闭包或生成式包装器的补丁版本,在跨多次访问的
===比较中可能会失败——这是原生实现绝不会出现的情况。
这些都无法证明「正确」的值本应是什么——只能证明回答这次查询的属性,已经不是浏览器出厂自带的那一个了。这往往比试图猜测某个特定 Canvas 哈希或 GPU 字符串「看起来对不对」更有力,因为它根本不依赖任何已知正确值的数据库。
为何粗糙伪装和反检测浏览器仍会失败
贯穿所有这些检查的一个反复出现的主题是:伪造单一信号很容易,但要一致地伪造一整套连贯的可信与不可信来源却并不容易。一个重写了 navigator.userAgent 的脚本,很少会同时给 Worker 上下文里的等价物打补丁、调整每一个下游属性描述符,并为刚安装的 getter 复刻出原生代码的 .toString() 输出。反检测浏览器——专门构建、旨在跨整个配置文件协调伪造值的 Chromium 分支——做得更接近完美,但它们同样往往会留下同一类缝隙:一个只在主线程打了补丁、Worker 内部却没打的值,一个响应方式与原生属性略有差异的 Proxy 陷阱,或者一个被可信来源检查悄悄拆穿的声称身份。
这套模型是长青的:某个指纹研究工具具体运行哪些检查,会随着引擎和伪造技术的演进而不断扩充,但其底层模型——通过可信与不可信两条路径读取同一事实,并把分歧当作证据——不会过时。这也正是基于连贯性的检测能够持久抵御一次性花招的原因:检测器不需要穷举每一种可能的谎言,只需要不断增加伪造工具必须一致打补丁的独立路径数量。
检查你自己的浏览器
BrowserInsight 自己的工具应用了同样的「一致性优先」思路,而不要求你去解读一个研究级的指纹仪表盘。指纹检测会呈现你的 Canvas、WebGL 和字体信号,以及标记它们是否对不上号的连贯性检查;Bot 检测工具则运行生产环境中缓解系统会用到的、聚焦于自动化与一致性的检查。如果你想要一个可以照着做的自查步骤版本,浏览器指纹一致性自查清单详细讲解了七项具体的 UA/GPU/时区/字体检查,几分钟内就能跑完。这些都不是教你如何通过CreepJS 或其他任何测谎工具的指南——理解这套模型的意义在于看清楚:真正被审计的是连贯性,而不是任何单一的值。
常见问题
CreepJS 会收集或存储我的指纹吗?
CreepJS 是一个客户端研究工具,你在自己的浏览器里运行它,看看它能揭示出关于你的哪些信息;自己运行它的意义在于审计你自己的信号,而不是被它追踪。在依赖任何托管的指纹识别服务之前,务必先查看该工具自己的隐私政策。
一个构建精良的反检测浏览器能骗过像 CreepJS 这样的测谎工具吗?
对部分检查而言,有时可以。要在一套不断演进的检查中,始终一致地协调每一个可信与不可信来源——主线程与 Worker 上下文、每一个属性描述符、每一个派生的副作用——是很难长期维持的,这也是为什么即使精心构建的反检测配置,通常也会至少留下一条缝隙,详见网站如何检测反检测浏览器和指纹伪造。
低信任分数能证明真实用户是机器人或欺诈者吗?
不能。测谎分数衡量的是指纹的连贯性,而不是意图。隐私扩展、较旧的浏览器版本以及不寻常但合法的配置,都可能触发个别检查项。生产系统会把这个信号与其他信号结合起来,而不会把它当作单独的定论。
测谎与普通的指纹唯一性测试有什么不同?
唯一性测试(EFF 的 Cover Your Tracks 是一个知名例子)衡量的是,在其他访客中,你组合起来的指纹有多容易被识别。而测谎测试问的是,构成这个指纹的各个信号,彼此之间以及与真实浏览器行为之间是否内部一致——这是一个完全不同的问题,根本不需要把你和任何其他人的指纹进行比较。


