2026/4/18 15:48:00
网站建设
项目流程
龙华做网站,百度扫一扫,电子商务网站的全面建设,网站招标书怎么做什么是设备指纹#xff1f;在讲实现之前#xff0c;先纠正一个误区#xff1a;设备指纹#xff08;Device Fingerprint#xff09;不是为了知道你是张三#xff0c;而是为了知道 这台设备是编号 9527。它的核心逻辑只有一条#xff1a;利用浏览器暴露的硬件底层差异在讲实现之前先纠正一个误区设备指纹Device Fingerprint不是为了知道你是张三而是为了知道这台设备是编号 9527。它的核心逻辑只有一条利用浏览器暴露的硬件底层差异显卡、声卡、电池、屏幕组合出极高熵值的唯一 ID。就算你清空了 Cookie换了 IP只要你的硬件没变这套代码算出来的 Hash 值就永远不变。下面我们直接上代码拆解最核心的三种实现方式。Canvas 指纹这是目前最成熟、识别率最高的技术。实现原理Canvas 绘图不仅仅依赖浏览器引擎它极度依赖底层的GPU 绘图指令、操作系统图形驱动以及抗锯齿Anti-aliasing算法。当你命令浏览器画一个红色的矩形里面写上 Hello, world!时NVIDIA 的显卡和 AMD 的显卡在处理边缘像素的混合抗锯齿时算法有微小的数学差异。Windows 和 Mac 在字体渲染Sub-pixel rendering上对笔画粗细的处理不同。这就导致了同一段 Canvas 代码生成的图片像素数据RGBA在不同设备上是完全不同的。核心代码实现方式我们不需要画多复杂的图关键是要触发差异。通常会用到光影叠加、异形字体、emoji检测字体库。function getCanvasFingerprint() { // 1. 创建一个不会挂载到 DOM 上的画布 const canvas document.createElement(canvas); const ctx canvas.getContext(2d); canvas.width 200; canvas.height 50; // 2. 文字干扰利用字体渲染差异 // textBaseline 设为 top/bottom 会触发不同的垂直对齐算法 ctx.textBaseline top; ctx.font 14px Arial; ctx.fillStyle #f60; ctx.fillRect(125, 1, 62, 20); // 背景块 // 3. 叠加混合触发 GPU 的颜色混合算法 ctx.fillStyle #069; // 写入带特殊符号的文字测试系统字体支持度 ctx.fillText(Hello, world! \ud83d\ude03, 2, 15); // 再次叠加利用 rgba 透明度触发抗锯齿差异 ctx.fillStyle rgba(102, 204, 0, 0.7); ctx.fillText(Hello, world! \ud83d\ude03, 4, 17); // 4. 导出指纹 // toDataURL 会返回 base64 字符串 // 同样的图像不同显卡生成的 base64 字符串的 CRC 校验码是不同的 const b64 canvas.toDataURL().replace(data:image/png;base64,, ); // 5. 将超长字符串 Hash 化 (这里用简单的 hash 示例生产环境可用 MurmurHash3) let bin atob(b64); let crc bin2hex(bin.slice(-16, -12)); // 取部分校验位 return crc; }差异在哪肉眼看这两张图是一模一样的。但如果你把两台电脑生成的 Base64 字符串拿去对比会发现可能在第 5000 个字符处有一个字母不一样。那就是显卡留下的签名。AudioContext 指纹既然显卡有差异声卡Audio Stack自然也有。原理Audio 指纹不是录音不需要麦克风权限。它是利用 Web Audio API生成一段数学上的声音信号正弦波、三角波然后经过一系列处理压缩、滤波。由于计算机浮点数运算的精度差异以及底层音频处理单元DSP的实现不同最终生成的PCM 音频数据流会有极其微小的差别。代码实现通常使用OfflineAudioContext离线音频上下文它可以在后台静默渲染音频不需要用户听到声音速度极快。function getAudioFingerprint() { // 兼容性处理 const AudioContext window.OfflineAudioContext || window.webkitOfflineAudioContext; if (!AudioContext) return null; // 1. 创建离线上下文1个声道44100采样率5000帧 const context new AudioContext(1, 5000, 44100); // 2. 创建振荡器 (Oscillator) // 三角波 (triangle) 比正弦波更容易暴露硬件处理的非线性差异 const oscillator context.createOscillator(); oscillator.type triangle; oscillator.frequency.value 10000; // 3. 创建动态压缩器 (Compressor) - 核心步骤 // 压缩器的算法在不同浏览器/硬件上实现差异很大 const compressor context.createDynamicsCompressor(); compressor.threshold.value -50; compressor.knee.value 40; compressor.ratio.value 12; compressor.reduction.value -20; // 4. 连接节点振荡器 - 压缩器 - 输出 oscillator.connect(compressor); compressor.connect(context.destination); // 5. 开始渲染 oscillator.start(0); context.startRendering().then(buffer { // 6. 获取渲染后的 PCM 数据 // 这是一个 Float32Array 数组 const data buffer.getChannelData(0); // 7. 计算 Hash let sum 0; // 简单累加所有采样点的绝对值作为指纹 for (let i 0; i data.length; i) { sum Math.abs(data[i]); } console.log(Audio Fingerprint:, sum); }); }不同设备跑出来的sum值会精确到小数点后十几位那个微小的尾数差异就是指纹。电池与硬件并发光有 Canvas 和 Audio 还不够因为同一型号的 iPhone 可能会完全一样。这时候需要引入动态硬件特征来增加熵值。电池电量 API (Battery Status API)注由于隐私争议太大Firefox 和 Safari 已禁用但 Chrome (部分版本) 和 Android Webview 中依然可能获取。这玩意的逻辑非常粗暴电量百分比, 充电/放电时间这个组合在特定时间点是极具唯一性的。// 核心代码 navigator.getBattery().then(battery { const level battery.level; // 例如 0.55 const chargingTime battery.chargingTime; // 例如 1200 (秒) const dischargingTime battery.dischargingTime; // 例如 Infinity // 指纹因子0.55_1200_Infinity // 结合 IP能把用户锁定得死死的 const batteryFingerprint ${level}_${chargingTime}_${dischargingTime}; });如果我在 1 分钟内连续请求两次发现你的电量从0.42变成了0.41这个变化曲线也是一种强指纹。硬件并发数与内存这些是Navigator对象上赤裸裸的硬件参数const hardwareInfo [ navigator.hardwareConcurrency, // CPU 核心数如 12 navigator.deviceMemory, // 内存大小 (GB)如 8 screen.width x screen.height, // 分辨率 screen.colorDepth, // 色彩深度如 24 window.devicePixelRatio // 像素比如 2 ].join(_); // 输出示例12_8_2560x1440_24_2虽然这些参数单看很普通但如果你把CPU 内存 分辨率 Canvas指纹 Audio指纹拼接在一起全球几十亿设备中能和你撞车的概率几乎为零。所谓的前端指纹技术本质上就是找不同的一种方式。开发者利用一切可以调用的 APICanvas, Audio, WebGL, 硬件信息强迫浏览器进行某种复杂的运算。由于硬件和驱动的细微差别运算结果必然存在差异。这些差异被收集起来生成了一个字符串。这就是浏览器在互联网上的唯一ID希望对你们有帮助。