浏览器指纹逆向怎么分析:从网络请求追踪到 JavaScript 采集链

浏览器指纹逆向,通常不是拿到一串哈希值后"解密出设备"。更可验证的任务是还原一条链路:

浏览器 API 返回原始值 → 页面脚本筛选和组合 → 生成待提交的数据 → 发出请求

这条链路中的每一步都需要单独找证据。脚本读取了 Canvas,并不等于 Canvas 结果进入了最终请求;请求带有一个 fp 字段,也不等于我们知道服务端如何使用它。浏览器指纹本身可以由浏览器及系统暴露的多种特征组成,例如语言、时区、屏幕信息和渲染结果。MDN 的定义可作为概念起点。

本文以自有页面或获授权的测试页面为分析对象,演示如何定位和记录客户端采集链。

一、先找输出:哪一次请求可能带走了环境信息?

打开 Chrome DevTools 的 Network 面板,再重新加载页面。建议从页面加载前开始记录,并先查看全部请求,不要过早只筛选 Fetch/XHR:提交方式和触发时机未确认前,筛选条件可能让你漏掉线索。

对候选请求依次检查:

  1. 触发时机:打开页面就发送,还是点击、登录或离开页面时发送?
  2. Payload:有无语言、时区、屏幕、图形信息,或经过处理的标识?
  3. Initiator:请求由哪个脚本或调用位置发起?
  4. 重复性:刷新页面后,哪些字段保持不变,哪些字段变化?

DevTools 的 Network 面板提供请求详情及发起位置等信息,具体入口可参阅 Chrome 官方文档。

请求名中的 device、risk、fingerprint 只能帮助缩小范围,不能单独证明它是指纹上报。反过来,请求名完全没有这些词,也不能排除它携带相关数据。

二、再找输入:脚本读取了哪些浏览器值?

通过候选请求的 Initiator 回到代码,在 Sources 中查看数据对象的生成位置。如果代码被压缩,可以先格式化,再围绕赋值和函数调用逐步追踪;必要时对相关代码或 XHR/fetch 调用设置断点。Chrome DevTools 断点文档列出了可用的断点方式。

观察时,把证据分成三类:

层次 要记录的内容 需要避免的推断
原始输入 API 名称、调用位置、实际返回值 "调用过"就等于"最终使用了"
中间处理 字段筛选、排序、序列化、编码或摘要计算 "生成了哈希"就等于"能反推出输入"
请求输出 请求体中的字段和值、发送时机 "已上报"就等于"知道服务端判定规则"

例如,看到 canvas.toDataURL(),可以确认代码在该位置导出了画布内容;还需要继续追踪返回值去了哪里。MDN 的 toDataURL() 文档说明了该方法的返回形式。

三、用一个本地示例理解"原始值"和"摘要值"

下面的代码只用于自己的 HTTPS 页面或本地测试页面 。它读取少量浏览器信息,计算 SHA-256 摘要并打印结果,不会发送网络请求:

复制代码
async function inspectFingerprintDemo() {
  const canvas = document.createElement("canvas");
  canvas.width = 200;
  canvas.height = 50;

  const ctx = canvas.getContext("2d");
  if (!ctx) {
    throw new Error("当前环境无法创建 Canvas 2D 上下文");
  }

  ctx.font = "20px sans-serif";
  ctx.fillText("fingerprint demo", 8, 30);

  const inputs = {
    language: navigator.language,
    timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    canvas: canvas.toDataURL()
  };

  const serialized = JSON.stringify(inputs);
  const bytes = new TextEncoder().encode(serialized);
  const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
  const digest = Array.from(new Uint8Array(hashBuffer))
    .map(byte => byte.toString(16).padStart(2, "0"))
    .join("");

  console.log({
    language: inputs.language,
    timeZone: inputs.timeZone,
    canvasLength: inputs.canvas.length,
    serializedLength: serialized.length,
    digest
  });
}

inspectFingerprintDemo();

这个示例刻意把两个状态分开:inputs 是可观察的原始值,digest 是根据序列化结果计算的摘要。SubtleCrypto.digest() 用于计算摘要,且需要安全上下文;详情见 MDN 文档。

真实页面可能使用不同的字段、顺序和处理方式。即使两处代码都写着 SHA-256,只要输入字段或序列化结果不同,输出也无法直接对照。因此,逆向分析时要记录参与计算的确切输入,而不只是算法名称。

四、请求找不到时,排查观察条件

如果 Network 面板没有出现预期请求,先检查记录是否覆盖了页面初始化全过程,以及是否被筛选条件隐藏。然后再观察交互或页面离开时机。某些页面可能使用 navigator.sendBeacon() 异步发送少量数据,因此仅盯着一个函数名或一种触发时机并不充分。MDN:sendBeacon()

如果找到了请求,却无法把请求字段对应到某个 API 返回值,不要急着为字段"猜含义"。可以在运行时逐步比较:

  • API 返回后,对象中出现了什么值?
  • 进入处理函数前后,值发生了什么变化?
  • 请求体保存的是原值、部分字段,还是处理后的结果?

找到一个相同字符串只能算线索。只有调用关系、运行时值和请求输出能够连起来,结论才较为可靠。

五、报告里怎样写,才不超出证据?

一个可复查的结论可以写成:

在本次测试条件下,脚本读取了语言、时区和 Canvas 导出结果;观察到其中若干值经过序列化及摘要计算;随后某次请求携带了处理后的字段。

若没有服务端代码或其他直接证据,就不应把它写成"网站一定依据这个哈希识别了某个人"。IP、Cookie、网络连接特征和服务端历史记录也可能参与系统判断,但它们不等同于这段 JavaScript 的采集结果。若要进一步区分浏览器可读取的信号、浏览器身份与长期保存的环境状态,可以参考 Web4 关于浏览器指纹与环境一致性的说明;具体页面实际使用了什么,仍须以该页面的代码和请求证据为准。

浏览器指纹逆向的终点不是猜出一个万能算法,而是交付一条可复核的证据链:输入是什么、在哪一步变化、最终输出了什么,以及哪些用途仍未得到证实。

相关推荐
智能运维指南1 小时前
企业运维监控平台哪家好?2026年五大全栈可观测方案对比与选型推荐
大数据·运维·人工智能
STQY燊桐启元(深圳)电子科技1 小时前
高导热型电磁抑制吸波材料:当“导热”不再是吸波片的附属功能
人工智能·导热片·电磁抑制吸波材料、·导热绝缘
Dawson Zhu1 小时前
【CUDA编程入门⑥】CPU多线程并行优化:OpenMP、NUMA与SIMD向量化
人工智能·语言模型·架构·aigc·agi
hudie7651 小时前
LikeShop 二开最常改的 5 个文件(附改前改后对比)
前端·uni-app·php·个人开发
今天背单词了吗9802 小时前
AI 网关:大模型时代的统一接入与安全防线
网络·人工智能·安全·ai·1024程序员节
碧海银沙音频科技研究院2 小时前
基于炬芯平台ATS308X多模型兼容语音识别算法
人工智能·算法·语音识别
女神下凡2 小时前
芯参谋(69):LED恒流驱动芯片电路设计指南
arm开发·人工智能·单片机·嵌入式硬件·设计规范
lazy H2 小时前
【计算机网络】第 5 篇:TCP 和 UDP 有什么区别?从 Java 后端场景理解两种传输协议
经验分享·后端·网络协议·tcp/ip·计算机网络·1024程序员节
张彦峰ZYF2 小时前
「能跑」不等于「可上线」:生产级 Agent 的验收标准
人工智能·agent·分层抽样、难例、信息不足、对抗·人工、规则、上一版本三条基线·go 与 no-go 决策·内测、灰度、全量三套标准