Canvas 指纹检测:空导出与错误状态
区分 Canvas 指纹检测中的 data:, 空结果、PNG 格式回退和 SecurityError,避免把导出失败当成像素差异。
BotBrowser Team
Canvas 指纹检测可能在取得可比较的像素结果之前,就没能生成图像。BotBrowser 文档提供 Canvas 噪声控制和 --bot-noise-seed,用于输出一致性的工作流。这些设置不能证明某次导出成功、使用了请求的编码器,或满足 origin-clean 要求。摘要缺失时,需要先保留导出状态,不能立即归因为浏览器身份问题。
TL;DR
data:,是空结果标记,不是空白 PNG。位图某个维度为零和序列化失败,可能返回相同的值。- MDN 还记录了位图尺寸超出实现上限时返回
data:,的情况。单靠这段字符串无法区分原因。 - 位图不满足 origin-clean 时产生同步
SecurityError;这一步先于零尺寸检查。 - 请求的图像类型不受支持时,可能成功回退到 PNG。得到 PNG 不代表请求的编码器正常工作。
toBlob()的结果形式不同:立即返回undefined,回调随后收到Blob或null。安全异常仍然同步抛出。
目录
图中表示的是文档规定的导出结果。它是依据来源整理的判断辅助图,不是运行截图,也不是检测器评分。
导出结果的含义
通常的 Canvas 指纹流程 将绘图、读回、摘要和后续比较分开。要把摘要比较解释为图像比较,必须先识别导出失败状态。如果检查器把捕获的任何异常或空结果都替换成同一个假值,显示结果携带的信息就少于底层 API。异常名称、位图尺寸和请求格式一旦被丢弃,便无法从这个假值中恢复。
HTML 标准将 toDataURL() 定义为 Canvas 位图的序列化操作。通常返回包含编码图像的 data URL,但精确字符串 data:, 也是规范规定的结果。这个最短的 data URL 表示空文本资源。它既没有 PNG 签名,也不能证明非空图像完成了编码。把它称为“空白图片”,会混淆没有编码图像与像素恰好透明的有效图像。
| 输入或阶段 | toDataURL() 结果 | toBlob() 结果 | 能够确认的事实 |
|---|---|---|---|
| 位图不满足 origin-clean | 同步 SecurityError | 同步 SecurityError | 导出时未满足安全前提 |
| 位图某个维度为零 | data:, | 回调收到 null | 这条路径没有序列化出图像 |
| 序列化未返回文件 | data:, | 回调收到 null | 编码器没有提供图像文件 |
| 请求类型不受支持,编码成功 | PNG data URL | PNG Blob | 回退编码成功 |
| 请求类型受支持,编码成功 | 图像 data URL | 图像 Blob | 生成了编码表示 |
MDN 补充了实现层面的情况:位图某个维度超过 Canvas 最大尺寸时,toDataURL() 返回 data:,。这个最大值不是跨环境通用常量。只保留空结果标记,无法区分超限、零尺寸和序列化器失败。接下来有用的证据是实际位图尺寸及导出阶段,而不是猜测图形设备。
BrowserLeaks Canvas 报告分析 对应特定的 PNG 与摘要路径。可见的支持状态不等于完整异常报告。文档规定的导出状态能区分可能被支持状态合并的结果,但不能据此断言 BrowserLeaks 展示了每个分支或保留了原始失败原因。
尺寸、来源清洁状态与异常顺序
Canvas 位图和元素的 CSS 显示框是不同对象。width、height 属性控制位图尺寸;CSS 可以缩放它的显示方式。截图上的大矩形不能证明编码位图同样很大。反过来,小元素也可能序列化远多于可见显示框所暗示的像素。仅凭截图作比较,可能把不同的位图输入误认为同一套对比配置。
零尺寸也不同于没有绘制内容。非零位图可以包含透明像素,并正常序列化为图像。data:, 对应某个维度没有像素,或无法取得序列化文件,而不是检查图案是否具有视觉内容。区分这些情况,才能避免在兼容性报告中把透明 Canvas 与零尺寸 Canvas 归为同类。
origin-clean 标记是另一项独立输入。两个导出算法都会先检查它。标记为假时,在执行后续序列化步骤之前抛出 SecurityError。位图同时存在其他问题时,顺序尤其重要:捕获到安全异常,不能直接改称零尺寸空结果。应用如果只记录“Canvas 不可用”,就会丢失实际停止的是哪一步。
MDN 对安全条件的描述,是位图内容来自或可能来自另一个源。这不能直接诊断某个具体图片请求。异常不会揭示完整绘图历史、影响位图的资源,或响应中有哪些标头。自有应用日志可以调查这些输入,但公开指纹面板不能仅凭一个错误标签推断它们。
改变元素的显示方式,并不能证明位图变成了 origin-clean。CSS 尺寸与安全标记属于模型的不同部分。同样,文档中的图片 URL 有效,也不能单独证明图片可以进入可读取的 Canvas 输出。显示资源与导出由它产生的像素,是不同的权限和操作。支持记录应保留导出异常,不能把显示成功当成导出必然成功的证据。
尺寸上限也需要分开判断。浏览器可能暴露 Canvas 接口,却无法导出某个具体尺寸的位图。功能存在、上下文创建、绘图和序列化是不同检查点。最后一步失败,不能证明前面的接口缺失。所引用的导出算法没有给出通用最大尺寸,因此跨平台报告应保留请求尺寸,不能把一个环境的限制写成所有浏览器的规范上限。
请求格式与序列化工作
请求的 MIME 类型是输入,不是返回表示的保证。HTML 序列化规则要求支持 PNG,并规定请求类型不受支持时回退到 PNG;带参数的类型也被视为不受支持。调用方期待 JPEG 时,不能因为方法返回了非空字符串,就认定使用了 JPEG。真正有意义的表示证据,是 data URL 的媒体类型或结果 Blob 的类型。
比较编码值之前就需要明确这一点。两个导出可以用不同文件格式表示等价的绘图,产生不同字节序列。即使格式不变,编码字节的摘要也只说明这些字节,不能证明两个解码器会显示不同像素。一般 Canvas 指纹流程包含绘图和读回;编码文件比较还引入序列化选择,不能默默把这些选择全部归因于图形表面。
可选参数 quality 适用于支持有损压缩的格式。MDN 描述的数值区间为 0 到 1;省略参数或超出这个范围时,用户代理使用默认质量。超范围数值在所引用的导出算法中不构成单独的质量错误分支,但尺寸、origin-clean 或序列化仍可能导致导出失败。不使用质量设置的格式,不会因为传入参数就增加该设置。因此,请求某个值不能证明 PNG 使用了它,也不能证明所有实现采用相同编码器配置。应把请求类型和质量与实际返回类型分别保留,避免将被忽略的输入或格式回退误认为渲染变化。
分辨率元数据又是另一条边界。导出文档描述了 96dpi 元数据,前提是格式支持编码这类分辨率信息。它是文件元数据,不是物理屏幕或元素 CSS 尺寸的测量。不能把序列化图像中的这个值改作屏幕密度观察。分开保留位图尺寸、CSS 布局与编码元数据,才能对比真正变化的输入。
toDataURL() 会在内存中为整张图像创建字符串。MDN 提醒,大图像可能带来性能问题,把字符串赋给图像 src 时还可能触及 URL 长度限制。这项提醒不能证明某次导出或后续 URL 赋值已经失败。报告若记录了给图像分配巨大 URL 后无法显示,应区分导出与后续消费操作;无法显示 URL,不等于序列化返回了 data:,。
toBlob() 避开了 data URL 字符串接口,但并不保证任何情况下都成功,也不是零内存操作。标准仍规定复制位图并执行序列化。它的立即返回值为 undefined,图像结果由回调接收。即使回调随后收到有效 Blob,把立即返回值记为“没有图像”也已经误读了 API 契约。回调中的 null 表示没有该图像结果;同步 SecurityError 则发生得更早,属于另一条路径。
指纹报告的对比场景
两条记录都包含 data:,,但位图尺寸不同。 一条可能有零维度,另一条可能请求了不受支持的尺寸,或遭遇序列化失败。字符串相同只说明空结果标记相同,不说明渲染指纹相同。判断是否同一原因之前,应保留 width 和 height。缺少这些输入时,合理分类是原因未明的空导出,不能称为匹配的设备签名。
请求 JPEG 后成功导出了 PNG。 文件成功生成,证明的是回退路径产生 PNG,而不是 JPEG 编码器处理了请求。将记录与 JPEG 结果归为一组前,应先比较请求类型与实际类型。对这些表示的摘要作比较,只能回答文件是否相同,无法独立确定最初的 Canvas 绘图是否有差异。
检查器从摘要变成了一个通用假值标签。 可见变化可能发生在导出失败之后,但通用标签不能指出分支。检查器源码或自有应用记录,需要确认究竟是捕获异常、拒绝空结果,还是更后面的图像步骤失败。BrowserLeaks 分析已经区分可见输出和完整失败诊断;同一边界也阻止了没有证据就把未知分支归因于隐私设置。
toBlob() 日志记录 undefined,随后回调记录了文件。 两个观察并不矛盾。立即返回值不是异步图像结果。应把回调结果与回调结果比较,不能拿一个 API 的立即返回值与另一个 API 的最终图像混为一谈。兼容性工单里,操作阶段应与方法名一起记录,合并不同导出实现的日志时尤其如此。
调整 CSS 尺寸似乎修复了页面,但导出尺寸没变。 显示结果和位图结果回答不同问题。布局调整可能改善呈现,却不改变序列化器接收到的位图。如果支持问题涉及可见元素,应保留两组尺寸;如果只涉及序列化,则保留位图尺寸。这能避免把输入没有变化的视觉修复,描述成指纹输入变化。
位图不满足 origin-clean,返回空结果前就抛出了异常。 异常顺序本身是证据。封装代码若把异常转换成假值,应记录这次转换,因为后续读者无法从假值还原同步异常。保留这种区别不需要原始像素;方法、阶段和异常名称已经足以把它与规范中的空结果路径分开。
最小诊断记录
有用的导出记录包含 method、位图 width 和 height、requestedType、actualType、outcome、phase。同步失败时,可以保留 exceptionName,而不保留图像。对于 toBlob(),必须分开立即调用与回调完成。这些字段描述操作及其结果,无需保存完整 data URL、编码图像或摘要。模式定义应用可以保留哪些字段;只有实际应用记录才能证明执行了哪个分支。
| 记录字段 | 保留内容 | 不能据此推断 |
|---|---|---|
width、height | 提供给导出的位图尺寸 | 物理屏幕尺寸 |
requestedType、actualType | 请求和实际生成的表示 | 支持所有请求编码器 |
phase | 调用、同步异常或回调完成 | 立即返回 undefined 就是图像失败 |
outcome | 图像、空结果标记、回调 null 或异常 | 设备身份或操纵行为结论 |
exceptionName | 可取得时实际观察到的异常类型 | 具体负责的跨源资源 |
如果报告只有捕获后的假值,应记录这种限制,不能凭预期填入 exceptionName。细节不可得与实际观察到异常不同。尺寸也是如此:不能把缺失的位图尺寸替换为后来测量的 CSS 显示框。派生字段可能有用,但需要自己的标签,避免记录在无声中变得比证据更精确。
BotBrowser 的 Canvas 文档 描述了 --bot-noise-canvas 和 --bot-noise-seed。这些控制对应像素输出一致性,不能证明具体导出生成了哪种表示,或位图是否通过安全前提检查。启动配置应与导出状态记录分开;设置相同,不代表后续指纹检测的所有输入都未变化。
最小记录还可以省略完整资源 URL、账户标识和图像内容。源码版本或应用构建标签,通常足以标识应用计划采用的导出路径。自有调查确实需要具体资源时,应遵循应用的保留策略,而不是把 URL 或像素放进公开报告。诊断价值在于保留分支,不在于积累可重复使用的图像签名。
常见问题:Canvas 指纹导出状态
data:, 是没有可见绘图的有效 PNG 吗?
不是。标准描述的是空文本资源。包含透明像素的非零位图属于另一种状态,可以被序列化为图像。这个空结果标记里没有可供比较的编码 PNG。
data:, 能证明 Canvas 指纹被阻止了吗?
不能。零尺寸和序列化失败都可能产生该结果;MDN 还描述了实现尺寸上限的情况。仅凭返回字符串,无法确认隐私设置或检测器如何解释失败。
不受支持的格式也能返回有效图像吗?
能。请求类型不受支持时,PNG 回退可能成功。应保留实际返回表示,不能把任何非空图像都当成使用了请求格式的证明。
SecurityError 能指出是哪张图片造成问题吗?
不能。它确认的是导出时位图不满足 origin-clean。异常不包含完整绘图历史,也不能独立确认负责的资源及其响应配置。
toBlob() 立即返回 undefined 是错误吗?
不是。这是方法规定的返回值。回调收到图像 Blob 或 null;同步安全异常与立即返回值、后续回调都不同。
所有浏览器都有同一个 Canvas 最大尺寸吗?
所引用的来源没有提供通用数值上限。应记录请求的位图尺寸和实际导出状态,不能把某个浏览器在某个环境中的最大值当成所有实现的共同上限。
data URL 相同能确认浏览器身份相同吗?
不能。相同的可能是编码图像,也可能只是空结果标记。两者都不能确认某个人、完整浏览器配置或外部站点的决定。即使只比较文件,也要先知道导出状态。
固定噪声种子能解释导出错误吗?
单独不能。种子控制针对像素输出一致性,而文档规定的导出状态取决于位图尺寸、origin-clean、表示方式与序列化。一个种子值不能证明满足了哪些条件,也不能证明图像完成了导出。
来源
- WHATWG HTML:Canvas 与位图序列化
- MDN:HTMLCanvasElement.toDataURL
- MDN:HTMLCanvasElement.toBlob
- BotBrowser Canvas 文档
实践结论
比较 Canvas 指纹结果前,先分类导出状态。data:,、同步 SecurityError、回调 null、PNG 回退和真正编码的图像,应分别保留。记录位图尺寸,以及请求与返回表示;支持问题不需要原始图像时就不保存。随后再参考更广泛的 Canvas 指纹流程 和 BrowserLeaks 报告分析,处理导出成功之后各自不同的阶段。
本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界