什么是浏览器指纹?
浏览器指纹,是用来唯一标识你浏览器的一组"特征值"。它不是我们理解中的那种真实指纹,而是通过收集浏览器、操作系统、设备分辨率、字体、插件等信息,组合成的一个独特 ID。
和传统的 Cookie 不同,浏览器指纹不需要在用户设备上存储任何东西,完全是"读取现有信息"来识别用户。
使用背景
在最近的项目中,有个小需求:想用用户的设备作为唯一凭证,来验证身份。
一开始我想着简单粗暴点,用 JS 获取手机的 IMEI 或 PC 的序列号。但查了下资料后才发现,这根本行不通------JS 根本没权限访问这些底层硬件信息,安全机制早就把这条路堵死了。
后来才反应过来,我真正想要的,是一个"设备唯一标识",也就是------浏览器指纹。
可行方案
查阅了一些资料之后,目前比较常见的几种浏览器指纹方案如下:
- Navigator 指纹:浏览器类型、版本、系统平台等信息。
- Canvas 指纹:让浏览器绘制一段隐藏的图像,然后读取图像的像素差异,不同设备会有微小区别。
- WebGL 指纹:利用显卡和图形驱动渲染差异,获取设备的唯一特征。
- 字体、插件、时区、屏幕分辨率等:这些信息组合起来也能提供一定的识别度。
当然,单一方案识别率可能不高,但多种信息结合后,指纹的唯一性就会明显提升。
Navigator 指纹
Navigator 是前端获取浏览器和部分设备环境信息的重要接口。
下面是一些常用的属性和方法(跨浏览器兼容性较好的为主):
属性/方法 | 作用说明 | 示例代码 |
---|---|---|
navigator.userAgent |
获取浏览器的用户代理字符串,可以用于判断浏览器类型、系统类型 | navigator.userAgent |
navigator.platform |
获取运行环境的操作系统平台类型(如 Win32、Linux x86_64、MacIntel) | navigator.platform |
navigator.appVersion |
获取浏览器版本信息和部分平台信息 | navigator.appVersion |
navigator.appName |
获取浏览器名称(大多数现代浏览器返回 "Netscape") | navigator.appName |
navigator.language |
返回当前浏览器的首选语言(如 "zh-CN"、"en-US") | navigator.language |
navigator.languages |
返回用户的首选语言列表 | navigator.languages |
navigator.hardwareConcurrency |
返回可用的逻辑处理器数量(CPU核心数) | navigator.hardwareConcurrency |
navigator.plugins |
返回当前安装的插件列表(仅桌面浏览器有意义,且有兼容性限制) | navigator.plugins |
navigator.onLine |
判断当前浏览器是否联网 | navigator.onLine |
navigator.cookieEnabled |
判断浏览器是否启用 Cookie | navigator.cookieEnabled |
navigator.geolocation |
提供地理位置定位服务(需要用户授权) | navigator.geolocation.getCurrentPosition(...) |
navigator.maxTouchPoints |
支持的最大触控点个数(触屏设备可用) | navigator.maxTouchPoints |
navigator.mediaDevices |
访问音视频设备管理 API(如获取麦克风、摄像头) | navigator.mediaDevices.getUserMedia(...) |
navigator.clipboard |
读写系统剪贴板(部分浏览器需要 https 环境和权限) | navigator.clipboard.writeText("Hello") |
navigator.connection |
获取网络连接信息对象(如带宽、类型,部分浏览器支持) | navigator.connection.effectiveType |
navigator.userAgentData |
在新标准中可用的一种用户代理信息对象,部分浏览器已支持,用户隐私性更高 | navigator.userAgentData |
偷个懒,让Tare直接帮我写个Navigator 指纹示例吧。

html
<!DOCTYPE html>
<html>
<head>
<title>Navigator 指纹示例</title>
</head>
<body>
<h2>Navigator 指纹示例</h2>
<pre id="output"></pre>
<script>
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 => b.toString(16).padStart(2, '0')).join('');
return { data, fingerprint: hashHex };
}
getNavigatorFingerprint().then(result => {
const output = document.getElementById('output');
output.textContent =
"采集到的 Navigator 信息:\n" + JSON.stringify(result.data, null, 2) +
"\n\n生成的指纹(SHA-256):\n" + result.fingerprint;
});
</script>
</body>
</html>
代码生成完毕,点击应用直接预览:

经过测试,在同一个电脑上,这个指纹是稳定的,多次执行,这个值不会变。
但这这个指纹明显有缺陷,我系统语言或者浏览器升级后,这个指纹肯定会改变。
Canvas 指纹
由于不同设备(包括操作系统、显卡、驱动、字体渲染引擎等)在绘制同一段 Canvas 内容时会存在细微差异,最终得到的图像数据(通常是像素或转成 base64)在不同设备上往往是不同的。
这些细微差异生成的哈希值就是"指纹",由于只与设备性能有关,指纹稳定性显然比Navigator 指纹高一些。

html
<!DOCTYPE html>
<html>
<head>
<title>简单Canvas指纹示例</title>
</head>
<body>
<h2>简单Canvas指纹示例</h2>
<p>请打开控制台(F12)查看结果</p>
<script>
// 创建一个简单的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 < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & 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 && window.crypto.subtle) {
const encoder = new TextEncoder();
const data = encoder.encode(result.dataURL);
window.crypto.subtle.digest('SHA-256', data)
.then(hashBuffer => {
// 将哈希缓冲区转换为十六进制字符串
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
console.log('Canvas指纹(SHA-256):', hashHex);
});
}
</script>
</body>
</html>
生成的指纹还是很不错的。

其他几种方式生成浏览器指纹都大同小异,这里就不介绍了。