蔡榕军 發表於 2025-8-7 18:12:00

记录---浏览器指纹-探究前端如何识别用户设备

<div>
<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-0">什么是浏览器指纹?</h2>
<p>浏览器指纹,是用来唯一标识你浏览器的一组“特征值”。它不是我们理解中的那种真实指纹,而是通过收集浏览器、操作系统、设备分辨率、字体、插件等信息,组合成的一个独特 ID。</p>
<p>和传统的 Cookie 不同,浏览器指纹不需要在用户设备上存储任何东西,完全是“读取现有信息”来识别用户。</p>
<h2 data-id="heading-1">使用背景</h2>
<p>在最近的项目中,有个小需求:<strong>想用用户的设备作为唯一凭证,来验证身份</strong>。</p>
<p>一开始我想着简单粗暴点,用 JS 获取手机的 IMEI 或 PC 的序列号。但查了下资料后才发现,这根本行不通——JS 根本没权限访问这些底层硬件信息,安全机制早就把这条路堵死了。</p>
<p>后来才反应过来,我真正想要的,是一个“设备唯一标识”,也就是——浏览器指纹。</p>
<h2 data-id="heading-2">可行方案</h2>
<p>查阅了一些资料之后,目前比较常见的几种浏览器指纹方案如下:</p>
<ul>
<li><strong>Navigator 指纹</strong>:浏览器类型、版本、系统平台等信息。</li>
<li><strong>Canvas 指纹</strong>:让浏览器绘制一段隐藏的图像,然后读取图像的像素差异,不同设备会有微小区别。</li>
<li><strong>WebGL 指纹</strong>:利用显卡和图形驱动渲染差异,获取设备的唯一特征。</li>
<li><strong>字体、插件、时区、屏幕分辨率等</strong>:这些信息组合起来也能提供一定的识别度。</li>
</ul>
<p>当然,单一方案识别率可能不高,但多种信息结合后,指纹的唯一性就会明显提升。</p>
<h3 data-id="heading-3">Navigator 指纹</h3>
<p><strong>Navigator 是前端获取浏览器和部分设备环境信息的重要接口。</strong></p>
<p>下面是一些常用的属性和方法(跨浏览器兼容性较好的为主):</p>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202508/2149129-20250807181013957-793255865.png" alt="企业微信截图_20250807180856" loading="lazy"></p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202508/2149129-20250807181017662-813434622.png" alt="企业微信截图_20250807180907" loading="lazy"></p>
<p>&nbsp;偷个懒,让Tare直接帮我写个Navigator 指纹示例吧。</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202508/2149129-20250807181034204-1682938477.png" alt="企业微信截图_20250807180918" loading="lazy"></p>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;
    &lt;title&gt;Navigator 指纹示例&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;h2&gt;Navigator 指纹示例&lt;/h2&gt;
    &lt;pre id="output"&gt;&lt;/pre&gt;
    &lt;script&gt;
      async function getNavigatorFingerprint() {
            // 收集 navigator 相关信息
            const data = {
                userAgent: navigator.userAgent,
                platform: navigator.platform,
                language: navigator.language,
                languages: navigator.languages,
                cookieEnabled: navigator.cookieEnabled,
                hardwareConcurrency: navigator.hardwareConcurrency || 'N/A',
                deviceMemory: navigator.deviceMemory || 'N/A',
                webdriver: navigator.webdriver || false,
            };
            // 将数据转成字符串
            const dataString = JSON.stringify(data);
            // 计算 SHA-256 哈希
            const hashBuffer = await crypto.subtle.digest(
                "SHA-256",
                new TextEncoder().encode(dataString)
            );
            // 转成十六进制字符串
            const hashArray = Array.from(new Uint8Array(hashBuffer));
            const hashHex = hashArray.map(b =&gt; b.toString(16).padStart(2, '0')).join('');
            return { data, fingerprint: hashHex };
      }
      getNavigatorFingerprint().then(result =&gt; {
            const output = document.getElementById('output');
            output.textContent =
                "采集到的 Navigator 信息:\n" + JSON.stringify(result.data, null, 2) +
                "\n\n生成的指纹(SHA-256):\n" + result.fingerprint;
      });
    &lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;</pre>
</div>
<p>代码生成完毕,点击应用直接预览:<br>
</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202508/2149129-20250807181053655-261180088.png" alt="企业微信截图_20250807180931" loading="lazy"></p>
<div>
<div>
<p>经过测试,在同一个电脑上,这个指纹是稳定的,多次执行,这个值不会变。</p>
<p>但这这个指纹明显有缺陷,我系统语言或者浏览器升级后,这个指纹肯定会改变。</p>
<h3 data-id="heading-4">Canvas 指纹</h3>
<p>由于不同设备(包括操作系统、显卡、驱动、字体渲染引擎等)在绘制同一段 Canvas 内容时会存在细微差异,最终得到的图像数据(通常是像素或转成 base64)在不同设备上往往是不同的。</p>
<p>这些细微差异生成的哈希值就是“指纹”,由于只与设备性能有关,指纹稳定性显然比Navigator 指纹高一些。</p>

</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202508/2149129-20250807181106562-841780368.png" alt="企业微信截图_20250807180941" loading="lazy"></p>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;简单Canvas指纹示例&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;简单Canvas指纹示例&lt;/h2&gt;
    &lt;p&gt;请打开控制台(F12)查看结果&lt;/p&gt;

    &lt;script&gt;
      // 创建一个简单的Canvas指纹生成函数
      function generateCanvasFingerprint() {
            // 创建canvas元素
            const canvas = document.createElement('canvas');
            canvas.width = 200;
            canvas.height = 100;
            
            // 获取绘图上下文
            const ctx = canvas.getContext('2d');
            
            // 填充背景
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // 绘制一些图形和文字
            // 绘制红色矩形
            ctx.fillStyle = 'red';
            ctx.fillRect(20, 20, 50, 50);
            
            // 绘制蓝色圆形
            ctx.fillStyle = 'blue';
            ctx.beginPath();
            ctx.arc(120, 45, 25, 0, Math.PI * 2);
            ctx.fill();
            
            // 绘制文本
            ctx.fillStyle = 'black';
            ctx.font = '16px Arial';
            ctx.fillText('Canvas指纹', 60, 80);
            
            // 获取canvas数据URL
            const dataURL = canvas.toDataURL();
            
            // 简单哈希函数
            function simpleHash(str) {
                let hash = 0;
                for (let i = 0; i &lt; str.length; i++) {
                  const char = str.charCodeAt(i);
                  hash = ((hash &lt;&lt; 5) - hash) + char;
                  hash = hash &amp; hash; // 转换为32位整数
                }
                return hash.toString(16); // 转换为16进制
            }
            
            // 计算指纹
            const fingerprint = simpleHash(dataURL);
            
            return {
                fingerprint: fingerprint,
                dataURL: dataURL
            };
      }
      
      // 生成并输出指纹
      const result = generateCanvasFingerprint();
      console.log('Canvas指纹:', result.fingerprint);
      console.log('Canvas数据URL前100个字符:', result.dataURL.substring(0, 100) + '...');
      
      // 如果浏览器支持更安全的哈希算法,也可以使用它
      if (window.crypto &amp;&amp; window.crypto.subtle) {
            const encoder = new TextEncoder();
            const data = encoder.encode(result.dataURL);
            
            window.crypto.subtle.digest('SHA-256', data)
                .then(hashBuffer =&gt; {
                  // 将哈希缓冲区转换为十六进制字符串
                  const hashArray = Array.from(new Uint8Array(hashBuffer));
                  const hashHex = hashArray.map(b =&gt; b.toString(16).padStart(2, '0')).join('');
                  
                  console.log('Canvas指纹(SHA-256):', hashHex);
                });
      }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
</div>
生成的指纹还是很不错的。<br>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202508/2149129-20250807181127269-1369655514.png" alt="企业微信截图_20250807180952" loading="lazy"></p>
<p>&nbsp;其他几种方式生成浏览器指纹都大同小异,这里就不介绍了。</p>
<p>&nbsp;</p>
<div>
<h2>本文转载于:https://juejin.cn/post/7511915154032853018</h2>

</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19027330
頁: [1]
查看完整版本: 记录---浏览器指纹-探究前端如何识别用户设备