本文分析 某皮 网页端(Web 浏览器环境)使用的设备指纹系统——longtoken。该系统运行在浏览器 JavaScript 环境中,通过采集上百项浏览器和设备属性,经过多层加密编码后生成唯一的设备标识 token,用于服务端识别和追踪访问设备。
与移动端(Android / iOS)的 SAP 签名体系不同,网页端指纹系统面临更大的技术挑战:浏览器沙箱限制了可获取的系统信息,JavaScript 语言本身的透明性使得算法更容易被分析,同时浏览器环境的多样性要求指纹具备更强的容错性和区分度。
某皮 网页端指纹系统由两个核心组件构成:longtoken(设备指纹令牌)和 capcha(验证码验证),两者共享部分密码学原语和加密逻辑,形成协同防护体系。
网页端指纹系统采用分层加密架构,整体流程如下:
┌─────────────────────────────────────────────────────────────┐ │ 浏览器环境 (Browser) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────┐ │ │ │ 属性采集层 │──▶│ 加密处理层 │──▶│ 编码输出层 │ │ │ │ │ │ │ │ │ │ │ │ 178+ 设备属性 │ │ RC4-like + │ │ gzip 压缩 + │ │ │ │ 浏览器 API │ │ AES-CBC + │ │ Base64 编码 │ │ │ │ 用户行为数据 │ │ RSA 密钥封装 │ │ 二进制序列化 │ │ │ └──────────────┘ └──────────────┘ └────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 指纹数据结构 (a1-a178) │ │ │ │ 每个字段采用 XOR 加密 + 位置标记 (mark) 封装 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 服务端验证 │ │ ┌─────────────┐ ┌─────────────┐ ┌────────────────────┐ │ │ │ Token 解析 │▶│ CRC32 校验 │▶│ AES 解密 + 属性比对 │ │ │ └─────────────┘ └─────────────┘ └────────────────────┘ │ └─────────────────────────────────────────────────────────────┘
longtoken 的生成过程可以概括为以下步骤:
longtoken 系统采集的设备属性分为多个维度,共计 178 个以上字段,覆盖了浏览器环境可获取的绝大部分设备信息。
系统采集浏览器的基础标识信息,包括完整的 User-Agent 字符串、浏览器版本、浏览器类型检测、platform 字段、productSub、语言配置(languages 数组和单一 language)等。通过对浏览器类型(如 Chrome、Firefox、Safari)的识别,服务端可以校验各属性之间的一致性。
系统通过浏览器 API 获取设备硬件特征,包括:
Canvas 指纹是网页端设备指纹的核心技术之一。系统采集了多种 Canvas 相关的哈希值:
这些 Canvas 指纹在不同设备上因 GPU、浏览器版本、抗锯齿算法等差异而产生不同的结果,具有较高的设备区分度。
系统检测浏览器对各类音频和视频编解码器的支持情况,包括 ogg、wav、xm4a、mp4a、xMpegUrl 等格式的支持级别(probably、maybe),以及 oggOpus、mp4、webm 等容器格式的支持。音频指纹通过 Web Audio API 生成 encodedData。
系统检测 WebGPU 相关的特性支持,包括:
系统采集 DOM 层面的指纹信息(dom_finger),以及浏览器环境变量(env 数组)、插件列表(plugin)、媒体设备列表(audioinput、videoinput、audiooutput)、语音合成引擎列表(speech_voice)等。
系统获取网络类型(network,如 wifi、cellular)、IP 信息(ip_info)、地理位置信息(location)等。
系统内置了针对 10 余种浏览器自动化框架的检测能力,这是该指纹系统的一个重要特征。检测的自动化工具包括:
检测方式包括检查全局对象属性、浏览器行为特征、事件 isTrusted 属性等多种手段。
系统通过检测浏览器 API 的异常行为来识别反爬虫工具,例如:
系统在指纹采集过程中嵌入时间测量,记录从开始到结束的耗时(a59 字段),通过异或常量混淆后存储。这种时间指纹可以检测执行环境是否因调试或自动化而导致执行速度异常。
系统通过 Battery API 获取设备充电状态(charger),包括是否充电、充电级别等信息。
longtoken 系统采用了多层加密体系,每一层使用不同的算法和密钥,形成了较为复杂的加密链路。
系统实现了一个基于 RC4 变体的自定义流密码,这是整个指纹加密体系的核心。
Encryptor 类的实现特点:
该密码的核心是一个固定初始化的 256 字节状态数组(state)。与标准 RC4 使用密钥初始化状态不同,该实现使用一个硬编码的固定排列作为初始状态:
state = [41, 82, 74, 163, 214, 66, 204, 104, 1, 87, 212, ...] // 共 256 个字节
这一设计意味着加密的"密钥"不在于状态初始化,而在于后续的加密过程。
在加密过程中,系统使用了预生成的函数数组(SBOX_ITEMS 和 arrayJOps),每个状态位置对应一个加密函数。这种设计在标准 RC4 中不存在,属于自定义修改。
加密函数 prgaCase 的计算方式为:
算法标准性判断:魔改(自定义修改)
该算法借鉴了 RC4 的框架,但使用了固定状态数组而非密钥初始化,并引入了预生成的函数数组来加速加密过程。与标准 RC4 的 KSA(密钥调度算法)完全不同,属于自定义修改的流密码。
系统在字段级加密中使用了一套独特的 XOR 加密加位置标记方案。每个字段在加密后都会附加一个标记头,标记头包含:
step_with_mark 函数的结构为:
[位置标记(2字节, XOR加密)] + [数据长度(2字节, XOR加密)] + [数据(N字节, XOR加密)]
step_encrypt_01 函数的结构略有不同,长度字段为 1 字节。
随机字符派生密钥:
XOR 加密使用的单字节密钥并非随机生成,而是通过以下方式派生:
"c944ad54224a0673101bcffb2a6ac28ca2d22edfbb32ff930e" 取模得到索引这种设计使得每次生成的密钥取决于时间戳,增加了重放攻击的难度。
在完成所有字段的 XOR 加密和拼装后,系统使用 AES-CBC 对整体数据进行第二轮加密。
AES 密钥生成:
AES 密钥通过随机字符串生成,取 Math.random().toString(36) 的切片并拼接至所需长度(默认 16 字节)。
非标准 IV 使用:
系统使用 AES 密钥本身作为 IV(初始化向量),这是非标准做法。标准 AES-CBC 应使用随机 IV 以确保相同明文在不同加密中产生不同密文。使用密钥作为 IV 意味着相同的数据和密钥总是产生相同的密文,降低了安全性但简化了解密流程。
算法标准性判断:标准 AES-CBC 算法,非标准 IV 使用方式
AES-CBC 算法本身是标准的(使用 CryptoJS 库实现,PKCS7 填充),但使用密钥作为 IV 是非标准的安全实践。
AES 密钥使用 RSA 公钥进行加密封装,确保只有持有私钥的服务端才能解密获取 AES 密钥,进而解密指纹数据。
RSA 公钥参数:
RSA 实现中包含了针对不同浏览器平台的优化:针对 "Microsoft Internet Explorer" 使用 30 位精度、"Netscape" 使用 28 位精度、其他浏览器使用 26 位精度的大数乘法实现。这是典型的多精度算术的浏览器适配。
算法标准性判断:标准 RSA(PKCS#1 v1.5 填充)
RSA 实现使用了标准的 PKCS#1 v1.5 填充方案(在明文前添加 0x00 0x02 + 随机非零字节 + 0x00),公钥指数 65537 也是 RSA 的标准选择。
系统使用标准 CRC32 算法计算数据完整性校验值。CRC32 多项式为 0xEDB88320( reflected 形式),这是 IEEE 802.3 标准多项式。
算法标准性判断:标准 CRC32
CRC32 实现使用标准多项式和标准查找表初始化,无自定义修改。
系统实现了一套基于时间的指纹生成机制,generate_time_array_with_mark 函数的工作方式如下:
关键特征:
在基准值生成时,系统会减去一个 30-40 之间的随机数作为偏移。这种随机偏移使得相同时间戳在不同调用中产生不同的指纹值,有效防止指纹重放。
算法标准性判断:自定义实现
时间指纹的生成算法(x/y 数组的选择逻辑、随机偏移)是 某皮 的自定义设计,不属于任何公开标准算法。Varint 编码则是一种通用的变长整数编码方式。
最终生成的 longtoken 由以下部分组成:
头部包含固定的魔数和版本信息:
| 偏移 | 长度 | 内容 |
|---|---|---|
| 0 | 1 | 固定值 31 |
| 1 | 1 | 固定值 23(头部长度) |
| 2 | 1 | 固定值 1 |
| 3 | 4 | 固定值 4 |
| 7 | 4 | 固定值 128 |
| 11 | 4 | 加密数据总长度 |
| 15 | 4 | CRC32 校验值 |
| 19 | 4 | SDK 版本号 |
加密数据体包含以下部分:
整个 Token(头部 + 数据体)经过 Base64 编码后作为最终的 longtoken 字符串输出。
capcha.js 与 longtoken 共享部分加密逻辑,但专注于用户行为数据的采集和验证。
Capcha 系统采集的用户交互数据包括:
Capcha 系统使用了一个自定义的 128-bit Hash 算法(my_h 函数),该算法基于 AES 的轮函数构建:
算法标准性判断:魔改(基于 AES 轮函数的自定义 Hash)
该算法借用了 AES 的轮变换结构(SubBytes、ShiftRows、MixColumns 的 T 盒实现),但使用自定义的 S 盒和轮密钥表,并加入了长度尾部标记。不属于任何标准的 Hash 算法(如 HMAC、CMAC 等)。
系统同时使用了标准的 xxHash32 算法计算数据哈希。xxHash 是一种非加密型快速 Hash 算法,以其出色的性能著称。
算法标准性判断:标准 xxHash32
xxHash32 实现使用了标准的种子值和常量(primes),与公开的 xxHash 规范一致,无自定义修改。
Capcha 系统在 longtoken 的基础上进一步增强了对浏览器自动化的检测,不仅检测自动化工具的存在性,还通过以下方式增强检测:
Capcha 生成的 Token 包含以下头部信息:
| 偏移 | 长度 | 内容 |
|---|---|---|
| 0 | 1 | 固定值 7 |
| 1 | 1 | 固定值 36 |
| 2 | 1 | 固定值 1 |
| 3 | 4 | 固定值 1 |
| 7 | 4 | RSA 加密密钥长度 |
| 11 | 4 | Payload 长度 |
| 15 | 4 | Body 的 xxHash32 值 |
| 19 | 4 | SDK 版本号(10412) |
| 23 | 1 | 随机字符串长度 |
| 24 | 4 | Body 加密时间(313 ± 30 的随机偏移) |
| 28 | 4 | 保留字段(0) |
| 32 | 4 | 配置类型长度 |
| 36 | 可变 | 配置类型字符串 |
最终输出包含 payload(Base64 编码的数据体)和 key(前 12 字节数据的 Base64 编码)两个字段。
longtoken 和 capcha 虽然服务于不同的目的,但在实现上有显著的共性和差异。
两者共享了以下组件:
| 对比维度 | LongToken | Capcha |
|---|---|---|
| 核心目的 | 设备唯一性标识 | 用户行为验证 |
| 属性数量 | 178+ 项静态属性 | 约 50 项动态行为数据 |
| 数据哈希 | 无独立哈希 | xxHash32 + 自定义 AES-Hash |
| 加密密钥来源 | SDK 版本号派生 | 随机 AES 密钥 |
| 时间指纹 | 基于固定数组的随机采样 | 基于实际交互时间戳 |
| 自动化检测 | 基础检测 | 增强检测(含脚本注入分析) |
| SDK 版本 | 5040200 | 10412 |
| 输出格式 | 单一 Base64 字符串 | payload + key 双字段 |
系统中使用的标准算法包括:
系统中经过自定义修改的算法包括:
对算法进行自定义修改的主要动机包括:
多维度指纹采集: 系统从 178 个以上维度采集设备属性,涵盖了浏览器、硬件、图形、音频、网络、DOM 等各个层面。即使部分属性因浏览器隐私模式或反指纹技术发生变化,其余属性仍能提供足够的区分度。
多层加密保护: 三层加密体系(XOR 流密码 + AES-CBC + RSA)使得指纹数据在传输过程中具有充分的保护。RSA 公钥加密确保只有服务端能获取 AES 密钥,AES-CBC 保护指纹数据的机密性,XOR 流密码在字段级别增加了额外的混淆层。
自动化框架检测: 系统对 10 余种主流浏览器自动化工具的检测能力覆盖了 Selenium、Puppeteer、Playwright、DrissionPage 等常见爬虫框架。检测手段包括全局对象检测、行为特征分析、事件 isTrusted 验证等多维度方式。
时间指纹防重放: 基于时间戳的指纹生成机制带有随机偏移,使得相同环境在不同时间产生的指纹值不同,有效防止了指纹的重放攻击。
字段级位置标记: 每个字段在加密后都附加了位置标记,使得数据结构具有自描述性,同时也增加了篡改的难度——修改任意字段都需要重新计算位置标记和 CRC32 校验值。
浏览器 API 异常检测: 通过检测原型链篡改、API 克隆能力异常等方式,系统能够识别出对浏览器 API 进行 Hook 或修改的反检测工具。
JavaScript 代码可见性: 作为浏览器端 JavaScript 实现,所有算法代码对访问者完全可见。虽然代码经过一定程度的混淆(如变量名压缩),但算法逻辑仍然可以被完整分析。这与移动端二进制文件的逆向难度有本质区别。
固定状态数组暴露: RC4-like 流密码的 256 字节初始状态数组硬编码在代码中,一旦代码可见,该密码的安全性完全依赖于 XOR 操作的混淆效果。
单字节 XOR 密钥空间过小: 字段级加密使用的 XOR 密钥仅为单字节(0-255),虽然密钥通过时间戳 CRC32 派生,但实际密钥空间仍然只有 256 种可能。
RSA 公钥固定: RSA 公钥硬编码在代码中,这意味着所有客户端使用相同的公钥。一旦公钥被获取(实际上它已经是可见的),攻击者可以尝试分析服务端行为。
环境一致性校验依赖服务端: 采集的 178 项属性的一致性校验完全依赖服务端逻辑。如果攻击者能够准确模拟目标环境的各项属性,则指纹系统无法在客户端侧进行自我验证。
某皮 网页端指纹系统在 Web 环境的技术约束下实现了较为全面的设备识别能力。178 项属性的采集覆盖面在同类方案中属于较高水平,多层加密体系和自动化检测机制提供了基础的安全保障。
然而,JavaScript 代码的完全可见性是该系统的固有弱点。与移动端二进制代码相比,网页端算法的分析门槛显著降低。系统的防护强度主要依赖于属性采集的广度和复杂度,而非加密算法本身的保密性。
对于一般水平的爬虫开发者,该系统的检测机制(尤其是自动化框架检测和浏览器 API 异常检测)构成了一定的技术障碍。对于具备深度逆向能力的攻击者,系统的主要防御手段转向了属性模拟的复杂度和成本。
移动端(Android / iOS)采用 SAP 签名体系,核心是请求签名验证;网页端采用指纹令牌体系,核心是设备唯一性标识。两者目标不同:移动端防止请求被伪造,网页端防止设备身份被冒用。
移动端使用的加密算法体系(ChaCha20、Salsa20、RC6、Threefish-512、ECDH 密钥交换、自定义 AES-GCM)在算法复杂度和加密强度上明显高于网页端。网页端受限于浏览器 JavaScript 运行环境和性能要求,使用了相对轻量的加密方案。
移动端算法编译为二进制代码或字节码,逆向需要专业的工具和技术;网页端算法以明文 JavaScript 形式存在,分析门槛显著降低。这是网页端指纹系统面临的根本性挑战。
移动端可以获取系统级别的硬件信息(IMEI、MAC 地址、序列号等),具有更强的设备唯一性保证;网页端受限于浏览器沙箱,只能通过浏览器 API 间接获取设备特征,区分度相对较低但覆盖面更广。
某皮 网页端 longtoken 设备指纹系统是一个在 Web 技术约束下实现的多维度设备识别方案。其核心技术特点包括:
系统的设计体现了在开放 Web 环境下平衡安全性、性能和可维护性的工程考量,同时也反映了网页端反爬虫技术从简单 Cookie 验证向多维度设备指纹识别的演进方向。


本文作者:回锅炒辣椒
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!