浏览器指纹不是浏览器内部的固定 ID,而是根据浏览器、操作系统和设备环境中的多个特征,组合计算出来的一种摘要结果。
本文使用原生 JavaScript 实现一个简化版本,适合用于前端实验、浏览器兼容性测试、安全课程演示和隐私研究。不涉及绕过平台检测或伪造浏览器环境。
一、浏览器指纹生成流程

基本流程如下:
采集公开特征
↓
统一数据格式
↓
计算哈希摘要
↓
多次测试稳定性
常见特征包括:
- User-Agent
- 浏览器语言
- 时区
- 屏幕尺寸
- 设备像素比
- 硬件并发数
- Cookie 是否启用
- Canvas 渲染结果
- WebGL 能力信息
MDN 将浏览器指纹描述为:网站收集浏览器和底层操作系统的区别性特征,并组合这些特征识别浏览环境。MDN:Fingerprinting
二、采集基础浏览器特征
function collectBasicFeatures() {
return {
userAgent: navigator.userAgent,
language: navigator.language,
languages: Array.from(navigator.languages || []),
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
platform: navigator.platform || '',
screenWidth: screen.width,
screenHeight: screen.height,
colorDepth: screen.colorDepth,
pixelRatio: window.devicePixelRatio,
hardwareConcurrency: navigator.hardwareConcurrency || null,
cookieEnabled: navigator.cookieEnabled
};
}
console.log(collectBasicFeatures());
这些字段主要覆盖:
- 浏览器和系统环境
- 语言与时区
- 屏幕显示参数
- 部分硬件信息
- Cookie 状态
单个字段通常不具备很高的区分能力,多个字段组合后才可能形成更有区分度的环境描述。
三、统一数据格式
为了避免字段顺序、空值和数组格式影响结果,需要对特征进行规范化处理:
function normalizeFeatures(features) {
return [
features.userAgent,
features.language,
features.languages.join(','),
features.timezone,
features.platform,
features.screenWidth,
features.screenHeight,
features.colorDepth,
features.pixelRatio,
features.hardwareConcurrency,
features.cookieEnabled
]
.map(value => String(value ?? ''))
.join('|');
}
使用方法:
const features = collectBasicFeatures();
const normalized = normalizeFeatures(features);
console.log(normalized);
只要输入字段保持一致,规范化后的字符串就应该保持一致。
四、使用 SHA-256 生成摘要
浏览器可以通过 Web Crypto API 计算 SHA-256:
async function sha256(text) {
const data = new TextEncoder().encode(text);
const buffer = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(buffer))
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
SubtleCrypto.digest() 会将可变长度数据转换成固定长度摘要,通常要求页面运行在 HTTPS 或 localhost 等安全上下文中。MDN:SubtleCrypto.digest()
五、生成 Canvas 特征
Canvas 指纹的基本思路是绘制固定内容,再读取绘制结果:
function getCanvasSource() {
const canvas = document.createElement('canvas');
canvas.width = 320;
canvas.height = 100;
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '18px Arial';
ctx.fillStyle = '#245';
ctx.fillText('browser-fingerprint-test', 10, 10);
ctx.fillStyle = 'rgba(80, 160, 220, 0.7)';
ctx.beginPath();
ctx.arc(200, 50, 28, 0, Math.PI * 2);
ctx.fill();
return canvas.toDataURL();
}
Canvas 的输出可能受到操作系统、字体、浏览器渲染引擎和图形环境影响。MDN:CanvasRenderingContext2D
由于浏览器可能限制 Canvas 读取,所以代码中应允许它失败:
function tryGetCanvasSource() {
try {
return getCanvasSource();
} catch (error) {
console.warn('Canvas 特征不可用:', error);
return null;
}
}
Canvas 只能作为辅助信号,不能单独代表设备身份。
六、完整示例
async function buildFingerprint() {
const basicFeatures = collectBasicFeatures();
const normalizedBasic = normalizeFeatures(basicFeatures);
const canvasSource = tryGetCanvasSource();
const payload = JSON.stringify({
basic: normalizedBasic,
canvas: canvasSource
});
const fingerprint = await sha256(payload);
return {
fingerprint,
basicFeatures,
canvasAvailable: Boolean(canvasSource)
};
}
buildFingerprint().then(result => {
console.log('浏览器指纹:', result.fingerprint);
console.log('基础特征:', result.basicFeatures);
console.log('Canvas 是否可用:', result.canvasAvailable);
});
如果要在页面中显示结果,可以使用:
<pre id="output">正在生成...</pre>
buildFingerprint().then(result => {
document.querySelector('#output').textContent =
JSON.stringify(result, null, 2);
});
七、如何测试指纹稳定性?

建议按照下面的顺序进行测试:
首次打开页面
↓
刷新页面
↓
重启浏览器
↓
清除 Cookie
↓
打开无痕窗口
↓
修改屏幕分辨率
↓
升级浏览器后再次测试
可以记录以下结果:
| 测试条件 | 指纹摘要 | Canvas 状态 |
|---|---|---|
| 普通窗口 | 记录结果 | 可用/不可用 |
| 页面刷新后 | 记录结果 | 可用/不可用 |
| 浏览器重启后 | 记录结果 | 可用/不可用 |
| 无痕窗口 | 记录结果 | 可用/不可用 |
| 修改分辨率后 | 记录结果 | 可用/不可用 |
需要区分两个概念:
- 稳定性:同一个环境在不同时间是否保持相近。
- 区分度:不同环境之间是否容易区分。
稳定并不等于唯一。浏览器升级、系统更新和隐私策略变化,都可能导致摘要改变。
八、常见问题排查
1. 每次生成结果都不同
可能原因:
- Canvas 读取受到隐私保护;
- 输入数据中包含动态值;
- 屏幕或窗口尺寸发生变化;
- 字段顺序不固定;
- 浏览器环境发生了变化。
可以先打印中间数据:
console.log(normalizedBasic);
console.log(canvasSource);
先判断是特征采集变化,还是哈希计算变化。
2. crypto.subtle 不可用
优先使用:
http://localhost
或通过 HTTPS 访问页面。
3. 不同设备生成了相同结果
这不一定是代码错误。基础字段较少时,不同设备可能拥有相同或相近的环境组合。
4. 清除 Cookie 后指纹仍然相同
这是正常现象。Cookie 和浏览器指纹属于不同机制,清除 Cookie 不一定会改变浏览器环境特征。
九、生产环境使用建议
如果这类代码用于正式项目,建议:
- 只采集完成业务目标所需的字段。
- 不默认请求位置、摄像头和麦克风权限。
- 不长期保存完整原始特征。
- 不把哈希摘要当作身份认证凭证。
- 不使用指纹作为唯一封禁依据。
- 在隐私政策中说明采集目的和保存周期。
- 评估不同浏览器的兼容性和误判情况。
如果需要管理多套相互隔离的浏览环境,也可以将 Web4 Browser 作为环境管理工具案例进行了解,但仍应自行核对 Profile 隔离、数据处理和权限边界。
十、隐私与合规边界
浏览器厂商正在通过减少可暴露信息、统一部分参数和限制接口访问等方式降低指纹识别风险。W3C:Mitigating Browser Fingerprinting
开发者需要注意:
- 哈希处理不等于匿名化;
- 浏览器指纹不等于真实身份;
- 指纹不一定永久稳定;
- 不应在用户不知情的情况下长期跟踪;
- 高影响业务决策不应只依赖浏览器指纹。
十一、总结
浏览器指纹生成可以概括为:
采集浏览器环境
↓
规范化特征数据
↓
生成 Canvas 等辅助特征
↓
使用 SHA-256 计算摘要
↓
通过多轮测试验证稳定性
它适合用于前端实验、兼容性分析、安全研究和隐私测试,但不是绝对唯一的设备编号,也不能替代账号认证、行为分析或人工审核。
正式项目还需要综合考虑浏览器兼容性、误判率、数据保存、用户告知和隐私合规要求。