BrowserLeaks Canvas 报告:信号与边界
从 Canvas 能力检查追踪 BrowserLeaks 的 PNG 摘要和签名查询,并区分它们与身份识别或机器人判定。
BotBrowser Team
在经过授权的 Canvas 检查中,BotBrowser 可以维持声明的浏览器配置一致,但它不能单独解释第三方报告,也不能把报告变成某个网站的机器人判定。BrowserLeaks 的 Canvas 页面把几种不同证据放在一起:API 能力、固定绘图生成的摘要、对其自有签名数据的查询,以及 PNG 文件详情。如果把所有行都看成一个指纹分数,就会看不出每个结果来自哪种操作,也看不出页面实际证明了什么。
TL;DR
- BrowserLeaks 在显示签名部分之前,分别检查 Canvas 2D、文本绘制和 PNG data URL 导出。
- 公开脚本绘制固定的
220×30图样,编码为 PNG,从解码后的字节计算 MD5 摘要,再请求/api/canvas/<digest>。 - 标为
Uniqueness的百分比由响应中的count和total计算;公开页面没有说明样本总体或数据收集周期。 - PNG 面板中的字节大小、颜色数、块和 CRC 都描述同一张生成图像,并非四种独立的浏览器身份。
- 查无记录、摘要共享、能力检查失败或摘要改变可能有不同原因。它们任何一项都不能单独识别个人,也不能证明浏览器是机器人。
目录
拆分报告中的证据层
公开页面把来自不同操作的输出放在一起。能力检查回答某个操作是否可用;签名查询回答一次绘图结果是否匹配服务响应中的某个键。Canvas 指纹概览介绍了绘图和像素读取如何生成浏览器信号;这里通过 BrowserLeaks 的公开页面标识和脚本,进一步区分与该信号并列展示的面板。
| 报告区域 | 页面中可见的操作 | 可以支持的有限结论 | 不能证明什么 |
|---|---|---|---|
| 能力行 | Canvas 2D 上下文、文本绘制和 PNG toDataURL() 检查 | 这些特定操作是否通过各自检查 | 所有 Canvas 功能都可用,或浏览器来自真人或自动化程序 |
| 签名 | 固定绘图被编码,其字节被归约为 MD5 值 | 本页用于查找签名的键 | 个人身份、账号匹配或通用浏览器标识 |
| 签名统计 | 响应提供 count、total、浏览器和操作系统标签 | 当前服务响应如何描述匹配的键 | 来源总体、收集周期或经过测量的机器人概率 |
| 图像详情 | 对生成文件的像素颜色和 PNG 块进行解析 | 这张绘图所生成文件的属性 | 与上述签名无关的独立指纹 |
页面标记为 canvas-support-2d、canvas-support-text 和 canvas-support-todataurl 的行分配了不同标识。签名和相同签名说明也有独立元素,文件大小、颜色数和 PNG 块表亦然。因此,源码给出了一个可核查的边界:一侧是功能是否可用,另一侧是数据集响应。若用单一的绿色或红色结论概括,就会抹掉这种区别。
canvas-support-todataurl 行不提供完整的失败诊断。Canvas 导出状态对照 将 data:,、同步 SecurityError 与成功的 PNG 回退分开,再解释图像摘要。
这个区分也适用于比较公开检测套件。公开检测套件实际检查什么根据各套件自己的章节区分页内检查和连接侧检查。BrowserLeaks Canvas 页面属于页面侧观察:它测试自己的 JavaScript 绘图和图像导出,而不是 TLS、代理路径或某个应用对账号的决定。
追踪签名计算过程
公开的 canvas.js 将画布宽度设为 220、高度设为 30。它绘制一个彩色矩形,再以不同填充样式在相邻位置写入字面字符串 BrowserLeaks,com <canvas> 1.0。绘图代码还会改变文本基线和字体设置。这些值定义了本项测试的输入,而不是 Canvas 的通用基准。更换文字、字体可用性、渲染栈或绘图尺寸都会形成不同输入。
随后脚本使用 toDataURL('image/png') 请求 PNG data URL,检查返回值是否以预期前缀开头,把 Base64 部分解码成字节,对字节计算 MD5 并显示摘要。接着它请求 /api/canvas/<digest>,用返回字段填充签名统计。这些步骤可从公开源码中看到;它们并不表示运行过 BotBrowser 会话,也不表示通过运行浏览器观察到了某个摘要。
这条处理链有几个边界。第一,键覆盖的是特定绘图和编码路径产生的字节,并不概括页面可用的所有 Canvas 操作。第二,MD5 在这里是紧凑的查询键;它的出现不代表图像已加密,也不保证隐私。第三,与摘要关联的响应描述的是该服务的数据,而不是其他网站的匹配逻辑。网站可以使用其他绘图、组合其他信号,或者完全不使用 Canvas。
不保存图像本身,也可以记录从源码得出的步骤:
| 阶段 | 记录内容 | 解读边界 |
|---|---|---|
| 页面和脚本 | URL 与获取日期 | 实现可能变化;不要把结论套用到未固定的版本 |
| 绘图输入 | 220×30、文字、字体、基线、填充样式和位置 | 输入变化不能证明渲染器变化 |
| 导出 | PNG data URL 是否被接受 | 导出能力不同于其他 Canvas API 是否可用 |
| 摘要 | 是否显示摘要,不保存原始图像 | 摘要是输出键,不是用户标识 |
| 查询 | 响应存在、没有匹配项或不可用 | 没有记录不等于自动化的否定分类 |
其他项目也会生成像素,但采用各自的方案。CreepJS Canvas seed 周期分析跟踪源码定义的一系列取值;它的 seed 和轮数不能替代 BrowserLeaks 的固定绘图。
解读页面的 Uniqueness 百分比
API 响应包含匹配记录时,脚本读取 count 和 total。它显示按 floor(100 × (100 − 100 × count / total)) / 100 计算的百分比,并把原始计数格式化为“count of total user agents have the same signature”。该计算是响应中匹配份额的补数。它既不是当前访问者独一无二的概率,也不是机器人检测器对该会话评分的校准指标。
源码没有说明响应中的 user agent 如何抽样、包含哪些浏览器版本、是否去重,也没有说明计数何时收集。缺少这些信息,就无法推断分母有多具代表性。审慎的表述应是“当前响应为其记录的 user-agent 集合报告了这一比例”,而不是“该浏览器在所有用户中独一无二”。如果查询没有返回记录,页面会将签名标为未列出;查无记录不能证明渲染结果罕见或新颖。
显示的浏览器和操作系统标签也来自响应值。代码把 response[digest].browser[0][0] 和 response[digest].os[0][0] 赋给页面元素。这些标签描述与键相关联的记录,并非直接读取某台物理计算机。它们或许能帮助理解服务如何分组签名,但不能验证发起当前请求的设备、配置、个人或应用。
隐私审查还应单独关注请求路径。页面把摘要发送到自己的 /api/canvas/ 路由以获取统计。URL 中的摘要仍是请求数据,可能出现在服务器日志或中间遥测中。把图像缩短为一个值并不会使请求匿名。不要向公共诊断页面提供敏感应用的图像或客户数据。页面输出来自 BrowserLeaks 的固定测试绘图,应与应用的私有 Canvas 分开。
单独查看 PNG 详情
图像面板检查的是签名步骤生成的同一张 PNG。脚本显示预览、报告编码后文件的字节大小,并调用 getImageData(0, 0, 220, 30) 统计该矩形中的不同像素值。它还把 PNG 字节传给解析器,列出块名称、长度、CRC 和选定内容。页面标记把这些行称作 “File Size”、“Number of Colors” 和 “PNG Headers”。
这些详情可以帮助排查导出或图像编码差异。如果两个文件的字节大小不同,可以先查看块表中的容器差异,而不必立即假设像素变化。如果颜色数不同,固定绘图或返回的像素值可能有所变化。但元数据不是第二种设备特征的独立证据:文件大小、颜色数、块结构和 MD5 都源自同一张图像。把每一项都当作独立匹配,会将一次观察放大成多个表面信号。
API 标准将 toDataURL() 描述为返回 Canvas 图像 data URL 的方式,并说明了可能影响结果的条件,例如 origin-clean 状态。BrowserLeaks 可见的能力行仅表示它自身的 PNG 导出检查。它没有报告所有可能的失败原因,也没有测试所有来源、图像、上下文或浏览器策略。应把“此页面的导出检查没有返回预期 PNG data URL”和更宽泛的“Canvas 已禁用”区分开。
记录受控比较
可复现的比较需要记录输入和页面版本,而不只是结果截图。记下 BrowserLeaks URL、获取日期、浏览器版本、操作系统配置、三项能力检查是否通过,以及签名查询是否返回记录。重复测试时,先保持条件一致再比较摘要。不要同时更换配置、浏览器版本、字体集合、视口和图形路径,然后把结果归因于其中某一项。
| 观察结果 | 接下来记录 | 不要推断 |
|---|---|---|
| 没有 2D 支持 | 浏览器版本、页面错误以及是否返回 2D 上下文 | 所有绘图或所有 Canvas 指纹都不可用 |
| 文本支持结果不同 | 具体能力行及使用的版本/配置 | 差异本身就是自动化信号 |
| PNG 导出未被接受 | toDataURL('image/png') 是否返回预期前缀及页面可见错误 | 浏览器没有 Canvas 实现 |
| 摘要有统计记录 | 报告的 count 和 total 以及来源日期 | 百分比代表整个互联网或机器人概率 |
| 摘要没有统计记录 | “响应中没有匹配记录”和页面版本 | 摘要是新的、唯一的或可疑的 |
| PNG 详情不同 | 文件大小、颜色数和变化的块 | 每个变化字段都是独立指纹 |
这份记录刻意比完整指纹配置范围更窄。它帮助获授权的团队重复一项公开测试,无须保留原始像素,也不把孤立结果变成定论。在条件固定时摘要稳定,意味着输出键未变;要证明编码字节相同,需要逐字节比较。这不能证明所有页面可见表面都稳定。摘要不同只说明输出键发生变化;在判断原因前,应先按表格比较条件。
常见问题
共享摘要是否意味着两个人是同一个人?
不是。它只表示固定绘图在所比较的观察中生成了同一个键。常见渲染结果可能共享该键,同一个人在更换浏览器、字体、操作系统或渲染方式后也可能得到不同结果。服务查询不能确定是谁发起了请求。
这个百分比衡量匿名性吗?
不衡量。公式来自服务响应中的 count 和 total。公开页面没有说明分母的抽样总体或周期,因此无法据此估计整个人群的匿名性。
BrowserLeaks 会返回机器人判定吗?
Canvas 页面显示能力、签名和查询信息。它的公开标记没有呈现机器人判定。其他服务可能组合不同证据,但这份报告不能证明这一点。
查询无法完成时
公开脚本把记录缺失和请求失败分开处理。响应中没有摘要键对应的记录,或者该记录的值是 not_listed 时,脚本不会填充签名统计。请求被拒绝或响应解析出错时,代码进入显示 “Data retrieval error” 并提供重试链接的分支。链接事件会阻止默认导航、显示加载状态并重新运行检查。这些是服务和请求状态,不是新的 Canvas 能力测试。
能力检查本身也有明确范围。页面先检查 2D 上下文,再检查 fillText,最后检查 toDataURL('image/png') 是否返回带预期前缀的字符串。如果测试绘图抛出异常,脚本会创建另一个 Canvas 并再次尝试;第二次失败会把本地检查标为未通过。可见行不会指出具体异常,因此 False 只把结果限定在这条代码路径上。它不能确定原因是来源策略、页面错误、浏览器配置还是实现问题。
这个区分会改变下一步诊断。遇到 not_listed 时,记录查询没有返回记录这一事实;只有研究可重复性时才比较同一固定绘图。遇到 “Data retrieval error” 时,先检查请求是否可用以及公开服务的响应。能力行失败时,记下三项检查中具体哪一项失败和确切浏览器版本;不要换用其他测试页面,却称结果复现了 BrowserLeaks 的流程。如果重试成功,只能说明上一次查询没有完成,不能说明 Canvas 发生了变化。
页面也会把摘要放在路径中请求 API。隐私审查应将这个请求与本地像素绘制分开考虑:执行查询时,服务器会收到这个特定图样的稳定键。公开源码揭示了路由形式,但没有说明服务器保留策略、日志或访问控制。不能假设该值只留在浏览器中,也不能假设它用于跨站关联;可见代码不足以支持这些说法。
公开来源
- BrowserLeaks Canvas 测试页面
- BrowserLeaks Canvas 页面脚本
- MDN:
HTMLCanvasElement.toDataURL() - WHATWG HTML Canvas 章节
- BotBrowser Canvas 文档
- BotBrowser 自动化一致性参考
实践结论
应把 BrowserLeaks Canvas 页面视为一项有文档依据、但输出分开的探针:能力、生成图像、签名查询和 PNG 检查。讨论百分比时保留分母和来源日期;查询无记录时就描述为无记录,不要把它说成判定。摘要用于复现页面的固定绘图;服务返回的标签不能验证身份,也不能决定访问是否由自动化发起。
本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界