返回知识中心
指纹

CreepJS CSS 计算样式字段说明

解析 CreepJS 对 CSSStyleDeclaration 键的收集、别名归一化,以及系统颜色和字体的独立计算值探测。

BotBrowser Team

文档中心

想直接进入 指纹 文档吗?

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

CreepJS 的 CSS 计算样式模块与 media query 模块彼此独立。固定版本的 src/css/index.ts 会建立属性名清单、归一化别名,并另外读取系统颜色和通用字体的计算值。BotBrowser 文档说明了由 profile 驱动的 CSS 设置,但没有声称实现此第三方模块或预测其输出。支持的属性名清单不是已观测样式值的清单,也不能证明设备身份。

CreepJS CSS 模块读取流程:枚举 CSSStyleDeclaration 原型与自身键,归一化别名,再分别通过计算样式读取系统颜色和通用字体

TL;DR

  • 固定版本实现读取 CSSStyleDeclaration 中可用的属性名,不会计算清单中每个属性的值。
  • 它合并原型名、可枚举自身键和识别出的别名,再用 Set 去重。
  • 系统颜色和通用系统字体通过 getComputedStyle() 分开探测。
  • CreepJS 将此模块与 media query 双通道分析中的 @media 和 matchMedia() 检查分开处理。

目录

模块读取的输入

源码调用 getComputedStyle(document.body),再把返回的 CSSStyleDeclaration 传入 computeStyle()。这个对象提供可用属性名,但源码不会遍历清单并保存每个属性的计算值。若 keys 中出现 color,它只说明当前环境的接口暴露了这个属性名,并不表示某个元素计算出了什么颜色。

源码还通过 Object.getPrototypeOf() 和 Object.getOwnPropertyNames() 读取声明对象的原型,并把原型自身的名字作为另一种输入。CSS 指纹检测指南讲的是 media query 与资源加载观测,而不是此声明对象的属性名清单。

这不是 CreepJS 的 CSS media query 模块。另一个实现通过 @media 自定义属性通道和 matchMedia() 检查偏好设置;它的十四组配对键回答的问题不同于这里的计算样式属性名清单。

属性名清单的组成

源码首先遍历 Object.keys(cssStyleDeclaration)。数字键被视为索引项:其值才是属性名,因此代码添加值而不是数字索引。非数字键会被添加,但以 -- 开头的自定义属性名除外。若索引项的值本身像自定义属性 token,也会从这条路径过滤。

CSS 属性可能同时使用连字符形式和 camelCase 形式,例如 background-color 与 backgroundColor。源码用 in 运算符检查对应形式是否存在,并标记对应项,避免把两种拼写当成互不相关的观测。它还会在查找时处理名称开头的前缀标记。

最终列表合并原型名、自身可枚举名称以及通过对应关系推导的名称,再由 Set 删除重复项。因此报告里的数量表示当前环境合并后的键清单,不是跨版本固定值,也不表示每个名字都有一个独立计算值。

输入源码操作最终条目的含义
原型Object.getOwnPropertyNames(proto)声明对象原型自身声明的名称
自身键Object.keys(cssStyleDeclaration)可枚举自身键;索引项转换成属性名
别名对应项对连字符/camelCase 形式执行 in 检查用于避免把别名算成不同属性的拼写关系
最终清单new Set([...])去重后的名称列表,不是值映射

固定版本 CreepJS CSS 模块源码是这些步骤的依据。CSSStyleDeclaration 标准参考解释了被检查的对象,但没有声称所有浏览器都会枚举相同名称。

可以把该模块看成一条短流程。第一阶段选择 getComputedStyle(document.body) 和 CSSStyleDeclaration 原型;第二阶段从原型和自身键建立名称清单,包含别名;第三阶段另行请求浏览器解析系统颜色和通用字体。三个阶段的输入和输出形态不同,不应压成一个“CSS 指纹”字段。

阶段源码操作输出形式可核对的问题
选择声明对象getComputedStyle(document.body)body 的声明对象来自哪个元素与文档?
枚举名称原型名称与 Object.keys()去重后的 keys 数组暴露的名称集合是否改变?
解析系统颜色设置关键词并读取 backgroundColorsystem.colors 中每个关键词的结果某个解析后的系统颜色是否改变?
解析系统字体设置通用 font 并读大小、族名system.fonts 中的大小/族名字符串系统字体简写的解析是否不同?

比较两个浏览器版本时,这个划分很实用。若 keys 改变,先问暴露的名称集合是否变化,而不是页面显示颜色是否变化。新增别名可能改变去重前的数组;原型位置变化可能影响输入项;Set 会隐藏重复拼写。反过来,名称清单不变时,计算颜色仍可能变化,因为该值单独保存在 system.colors。

保留报告的层级结构。CSS 对象包含计算样式结果和系统样式结果;界面还会显示短 hash 和数量。数量是有损摘要,不会保留具体名称、别名处理过程或探测值。比较时应留存底层对象,而不是只抄录数字。

在自有页面上检查,不虚构基线

对自己控制的页面,可以用下面的控制台采集方式分别保留这些类别。它是阅读辅助,不是 CreepJS 所有 helper 的完整复现,也不代表其他服务采用同样的数据:

const declaration = getComputedStyle(document.body);
const prototype = Object.getPrototypeOf(declaration);
const ownNames = Object.keys(declaration).map(key => {
  const numeric = !Number.isNaN(Number(key));
  return numeric ? declaration[key] : key;
});
const prototypeNames = Object.getOwnPropertyNames(prototype);
const inventory = [...new Set([...prototypeNames, ...ownNames])];
const selectedValues = Object.fromEntries(
  ['color', 'background-color', 'font-family', 'font-size'].map(name => [name, declaration.getPropertyValue(name)])
);
console.log({ inventory, selectedValues });

示例把值样本命名为 selectedValues,不声称完整复制 CreepJS 的别名逻辑,也不替代其系统颜色和字体探测。固定源码中的这两组探测会创建或复用元素、设置内联样式关键词、调用 getComputedStyle(),再读取指定字段。解释结果前应记录实际检查的元素,以及是否修改过它。

比较运行记录时,将浏览器构建版本、页面 origin、目标元素、采集时间和原始对象分开保存。名称数组与值分别对比。保存前不要排序或归一化原始对象,否则可能抹去源码实际返回的表示形式。可以另建归一化视图,但应保留原始数据。

缺失值也应单独记录。属性可能不受支持,读取可能返回空字符串,源码也可能在创建预期对象前报错;这些状态并不等价。CSSOM 规定属性访问和计算样式的 API 行为,但记录仍须标明字段是缺失、空值还是有内容,避免把缺失观测改写成测得的阴性结果。

别名归一化有边界

源码里的对应项检查用于去重,不证明所有 CSS 写法在所有上下文中都可互换。代码会为含连字符的名称推导对应形式,处理开头的前缀标记,也会从大写字母推导连字符写法。若声明对象中存在对应项,源码会将其标记在 propertiesInPrototypeChain。最终列表会纳入该名称,再由 Set 移除完全相同的重复项。

比较报告时有两个不同问题:此实现是否把两个字符串视为别名,以及在某元素上读取两种写法是否得到相同值。源码在建立名称清单时处理第一个问题,但没有为每组别名保存值比较。因此不能因为名称经过归一化,就推断它测试过值等价性。

自定义属性也走不同路径。符合 --... 的名称不会从普通分支添加;某些类似自定义属性 token 的值会从索引路径排除。这是此源码自己的过滤决定,不代表自定义属性不能通过 getPropertyValue() 读取;keys 不是所有声明或样式表内容的完整导出。

观测到的变化首先查看不要据此断言
keys 数量变化,但常见名称仍在别名对应集合和原型枚举样式值按相同比例发生变化
出现新的连字符/camelCase 写法名称转换与 in 检查两种写法的值都被比较过
system.colors 变化关键词设置及计算后的 backgroundColor唯一原因一定是系统主题
system.fonts 变化通用 font 设置、解析出的大小与族名已枚举所有安装字体
CSS 报告缺失或不完整错误捕获与渲染路径浏览器返回了空清单

这张表是根据固定实现整理的排查地图,说明各类输出来自哪里。没有原始对象和环境元数据时,它不能诊断某次具体差异。

系统颜色与字体是独立探测

getSystemStyles() 分别执行两组调用。对于 Canvas、CanvasText、ButtonFace 和 Highlight 等颜色,源码把 background-color 设置到临时元素,再读取 getComputedStyle(el).backgroundColor。对于 caption、icon、menu、message-box、small-caption 和 status-bar 等通用系统字体关键词,源码设置 font 简写,并记录计算后的 fontSize 与 fontFamily。

这些是值观测,与 keys 名称清单不同。它也不是颜色解析器:源码让浏览器解析系统颜色关键词,再保存返回的计算颜色字符串。CSS 系统颜色参考说明关键词类别,getComputedStyle()说明读取已解析样式的 API。

若没有传入元素,源码会创建 div、追加到 document.body、读取样式后移除;传入元素时则复用该元素。函数会捕获异常,并经 CreepJS 错误路径报告后不返回系统样式结果。因此结果缺失不等于测得空列表。

固定关键词列表也界定了源码未执行的操作。颜色列表含 Canvas、CanvasText、Field、FieldText、Highlight 和 WindowText;字体列表是通用系统字体关键词,不是所有已安装字体的目录。它回答的是浏览器如何在选定元素上解析这些关键词,而不是安装了哪些字体,也不是 matchMedia() 返回了何种偏好。

源码先把颜色关键词设为 background-color: <keyword> !important,再读解析后的背景。字体关键词通过 font 简写设置,然后将解析后的大小和族名组合成字符串。这些选择定义了测量面。对比记录应保留关键词、字段名和返回字符串,否则 CanvasText → rgb(...) 可能被误认为任意页面文本颜色的直接读取。

DOM 临时状态也重要。默认路径追加并移除源码创建的 div;它不同于受 class、继承样式或内联声明影响的应用元素。源码明确选择了默认元素,复现时不要悄悄换元素后把差异归因于浏览器。

属性名清单不能说明什么

属性清单不能证明页面使用过某属性、返回过某个值,或远端检测器消费了报告。源码只能表明它记录哪些名称、请求哪些系统颜色和字体值。它本身不能识别硬件、操作系统或个人。名称存在只是接口层观测;解释时还需要浏览器版本、文档上下文和真实值。

BotBrowser 在 CSS 信号一致性文档中说明了 CSS 相关 profile 设置。该页讲的是配置,不声称实现此第三方模块,也不预测其他服务如何解释其结果。产品能力说明和 CreepJS 源码结论应分开。

常见问题

keys 包含所有计算后的 CSS 值吗?

不包含。它是从声明对象的键和别名整理出的名称。示例源码只在 getSystemStyles() 中另行请求系统颜色和字体值。

为什么要检查对象和原型?

源码把可枚举自身键与原型自身名称作为不同输入,然后合并。这样可以看到源码如何归一化名称,但不表示每种输入分别对应一次浏览器测量。

它与 matchMedia() 是同一信号吗?

不是。src/css/index.ts 读取声明名称与系统样式;src/cssmedia/index.ts 通过 CSS 规则和 matchMedia() 检查偏好。它们都涉及 CSS,不代表属于同一个模块。

这份清单能识别设备吗?

不能。名称描述 API 表面,系统样式是浏览器解析出的值;任一项都不能单独证明物理设备或唯一身份。

来源

实践结论

阅读 CreepJS CSS 报告时,应区分两类数据:合并后的属性名称清单,以及独立计算的系统颜色和字体值。对比时保留类别边界、固定源码版本并保存真实对象,不要从一个计数推断其余内容。media query 双通道文章介绍偏好通道;CreepJS 字体表分析介绍报告中的另一字段,不等同于系统 CSS 字体值。

#CreepJS#CSSStyleDeclaration#计算样式#浏览器指纹识别#隐私

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

在 BotBrowser 里跑一遍同样的检查

加载 profile,让刚读到的这些信号在浏览器内部保持一致,并在正式上线前先在自己的机器上确认结果。