返回知识中心
指纹

密码学随机性可观测性与浏览器指纹

用可复现 fixture 对比 Web Crypto getRandomValues 与 crypto-js 随机数实现,并限定指纹观察的隐私边界。

BotBrowser Team

文档中心

想直接进入 指纹 文档吗?

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

BotBrowser 可以重放一个授权的同源 fixture,记录 API 可用性和有限的输出元数据,但不能证明熵质量、识别个人,也不能把随机值变成稳定的浏览器指纹。可用的比较是:浏览器负责的 crypto.getRandomValues() 合同,与向库级生成器提供随机字节的 crypto-js 公开源码路径。

BotBrowser 可以在授权的同源夹具中重放随机性检查,比较有限的能力和长度字段,并在声明的 profile 或依赖版本变化后指出第一处差异。它不能证明熵质量、从随机样本推断个人,也不能保证库的备用路径适合密码学材料。

TL;DR

  • crypto.getRandomValues() 是填充类型化数组的浏览器 API;它不会暴露可复用的浏览器身份。
  • crypto-js 是应用代码。它的随机数路径取决于版本、构建、runtime 全局对象,以及实际加载的源码分支。
  • 比较能力、源码版本、数组形状和粗粒度输出元数据。不要公开随机值、seed、账户数据或原始熵样本。
  • 重复 fixture 可以发现实现变化或备用路径失效,但单次输出不能证明熵质量或指纹唯一性。

Contents

流程图将 WEB_CRYPTO 与 CRYPTO_JS 和有限记录、隐私边界分开。

区分两个随机源

Web Cryptography 规范 将 Crypto.getRandomValues() 定义为浏览器对整数类型化数组执行的操作。MDN 参考 记录了支持的数组类型、同步返回和配额边界。底层来源由浏览器持有,错误的视图可能被拒绝;页面代码不会选择 seed。

crypto-js 的所有权模型不同。它的公开源码树包含供库算法使用的随机 word helper、runtime 能力检查和在不同版本间变化的 respaldo。读取实际进入 bundle 的准确版本源码,然后记录它选择了浏览器 crypto 对象、其他 runtime 提供者还是 respaldo。仅凭包名不能判断分支。

这里比较的是可观察合同,而不是复现内部熵。浏览器 fixture 可以记录 crypto.getRandomValues 是否存在、类型化数组是否填充,以及加载的 crypto-js 构建是否返回预期形状。它不能把任一结果变成稳定的设备标识。相关信号检查可参阅浏览器指纹验证和跨 realm 的浏览器 API 完整性。

把源码链接与 fixture 放在一起:crypto-js 随机源码、crypto-js releases 和 BotBrowser 自动化一致性。对应 docs 页面覆盖一致性检查,但没有覆盖这组源码级比较或脱敏规则。

使用可复现 fixture

在声明的新 profile 中运行同一个 HTTPS 页面。下面的 fixture 只记录分支名称、长度和生成字节的短 digest;不会保存随机值本身。

async function observeRandomness() {
  const record = {
    fixtureId: 'crypto-randomness-01',
    browserCrypto: typeof globalThis.crypto?.getRandomValues === 'function',
    cryptoJsLoaded: typeof globalThis.CryptoJS !== 'undefined',
    browserLength: null,
    libraryLength: null,
    digestPrefix: null,
    outcome: 'pending',
  };

  try {
    if (record.browserCrypto) {
      const bytes = new Uint8Array(32);
      globalThis.crypto.getRandomValues(bytes);
      record.browserLength = bytes.byteLength;
      record.digestPrefix = Array.from(bytes.slice(0, 4), value => value.toString(16).padStart(2, '0')).join('');
    }
    if (record.cryptoJsLoaded && typeof CryptoJS.lib.WordArray.random === 'function') {
      const words = CryptoJS.lib.WordArray.random(16);
      record.libraryLength = words.sigBytes;
    }
    record.outcome = record.browserCrypto || record.cryptoJsLoaded ? 'observed' : 'unsupported';
  } catch (error) {
    record.outcome = error.name === 'QuotaExceededError' ? 'quota-error' : 'api-error';
  }
  return record;
}

fixture 可复现,是因为输入固定:fixtureId、32 字节浏览器数组、16 字节库请求和相同的已加载包版本。输出故意不是确定性的。可交接的 JSON 示例:

{
  "fixtureId": "crypto-randomness-01",
  "browserCrypto": true,
  "cryptoJsLoaded": true,
  "browserLength": 32,
  "libraryLength": 16,
  "digestPrefix": "redacted",
  "outcome": "observed"
}

改变一个变量后重复 fixture:浏览器构建、profile、包版本、bundler 或 runtime。私有运行记录保留 browserVersion、os、profile、cryptoJsVersion 和 testedOn。不要比较随机字节;比较第一个变化的能力或形状字段。

行单一变化主要字段负责人
Abaselinefixture 的全部字段测试负责人
B浏览器构建browserCrypto、browserLength、outcome浏览器/上下文
Cprofile 生命周期能力和结果profile
Dcrypto-js 版本cryptoJsLoaded、libraryLength、outcome依赖负责人
Ebundler 输出加载和方法路径构建负责人

排障与边界

让每次 Web Crypto 请求都不超过字节数限制

Web Crypto 规范要求:类型化数组的 byteLength 超过 65,536 时,getRandomValues() 必须抛出 QuotaExceededError。应检查字节数,而不是元素个数:Uint32Array(16_384) 恰好是 65,536 字节,再多一个元素就会超限。下面的函数只接受 Uint8Array,因此子视图中的元素数也等于字节数;更宽元素的视图应先转换为字节视图:

function fillRandomBytes(output) {
  if (!(output instanceof Uint8Array)) {
    throw new TypeError('output must be a Uint8Array');
  }
  const maxBytesPerCall = 65_536;
  for (let offset = 0; offset < output.byteLength; offset += maxBytesPerCall) {
    const length = Math.min(maxBytesPerCall, output.byteLength - offset);
    globalThis.crypto.getRandomValues(output.subarray(offset, offset + length));
  }
  return output;
}

这只处理 API 的单次调用大小限制;它不会让更大的缓冲区变成确定性结果,不会验证熵质量,也不会让随机字节适合公开。保留 fixture 的错误分类:请求过大属于请求形状错误,不代表随机源较弱,也不是浏览器身份的证据。

如果 browserCrypto 为 false,先检查 origin、realm 和上下文,再改变 profile。如果只有库字段变化,检查解析后的包和加载顺序。quota-error 说明请求大小被拒绝,不代表熵较弱。不要把 digest 前缀当作指纹,只保留有限字段。BotBrowser 可以重放授权 fixture 并比较第一处变化,但不能证明熵质量或个人身份。

不过度解读观察结果

观察结果可以支持的判断不能证明的事项
browserCrypto: true页面可以调用浏览器 API熵质量、硬件来源或个人身份
libraryLength: 16已加载库返回了请求形状哪个提供者提供了字节
quota-error浏览器拒绝了请求的数组大小profile 损坏或随机性较弱
cryptoJsVersion 分支变化值得检查的源码或依赖变化浏览器指纹或安全结论

如果浏览器字段从 true 变为 false,先检查上下文和页面隔离,再归因于 profile。如果只有库字段变化,核对最终 bundle 的模块入口和初始化顺序。如果 digest 前缀重复,把它视为需要重跑的脱敏 fixture 产物,而不是稳定身份的证据。Web Crypto 建议 和 crypto-js 源码回答不同问题,应保持为独立引用。

保持指纹与随机性分离

指纹是相对稳定或受策略控制的观察集合,例如 user-agent 数据、屏幕指标或功能可用性。随机输出则是有意变化的。把随机样本与 profile 关联可能产生应用标识,但这个标识来自应用数据模型,而不是 getRandomValues()。

对于最小化遥测,保留 fixture id、API 分支、请求长度、包版本、粗粒度结果和时间戳。删除随机值、seed、完整 digest、账户标识和跨站关联。BotBrowser 可以在授权的同源夹具中重放随机性检查并比较 available、fallback、reused 或 error 等有限状态;它不能认证第三方熵测试、从样本推断个人,也不能保证库的备用路径适合密码学用途。

下一篇阅读

参阅确定性浏览器行为和隐私保护验证方法,分别理解可重复性与数据最小化。

观察项用途建议保留建议删除
available 或 error兼容性支持fixture id 和粗粒度状态原始字节和堆栈
请求数组长度回归排查长度和浏览器主版本完整样本值
seeded fixture digest重复性检查fixture 修订和仅用于测试的 digest生产账户关联
token 重用次数生命周期问题次数和过期区间token 文本和 endpoint URL
备用分支产品决策分支名和应用修订页面使用的用户数据

实践结论

将浏览器的 getRandomValues() 与 bundle 实际加载的 crypto-js 精确公开源码进行比较。使用固定 fixture,只保留能力和形状元数据,每次重跑只改变一个变量。结果可以解释源码或 runtime 的变化,但不能把随机性变成指纹,也不能证明熵质量。

Public sources

BotBrowser 支持在授权的同源夹具中重放随机性检查,并比较 available、fallback、reused 或 error 等有限状态。BotBrowser 不能证明熵质量,也不能从随机输出推断个人。

#Web Crypto#getRandomValues#Crypto-Js#Browser Fingerprints#Randomness

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

让 BotBrowser 从研究走向生产

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