JS 获取浏览器指纹
浏览器指纹是网站用来识别和追踪用户的一组设备与浏览器特征参数。通过 JavaScript,我们可以在用户访问页面时采集这些参数。本文介绍常用的指纹采集方法与代码实现。
基础环境参数
以下参数可以直接通过 navigator 和 screen 对象获取:
function getBasicFingerprint() {
return {
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
languages: navigator.languages,
cookieEnabled: navigator.cookieEnabled,
onLine: navigator.onLine,
screenWidth: screen.width,
screenHeight: screen.height,
screenColorDepth: screen.colorDepth,
screenAvailWidth: screen.availWidth,
screenAvailHeight: screen.availHeight,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
timezoneOffset: new Date().getTimezoneOffset(),
deviceMemory: navigator.deviceMemory || 'unknown',
hardwareConcurrency: navigator.hardwareConcurrency || 'unknown',
maxTouchPoints: navigator.maxTouchPoints || 0
};
}
Canvas 指纹采集
Canvas 指纹通过绘制特定图形并读取像素数据生成哈希值。不同显卡、驱动和浏览器会产生不同的渲染结果。
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 200;
canvas.height = 50;
// 绘制文本和图形
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(0, 0, 200, 50);
ctx.fillStyle = '#069';
ctx.fillText('EasyBR Canvas Fingerprint', 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText('EasyBR Canvas Fingerprint', 4, 17);
// 添加复杂图形增加差异性
ctx.beginPath();
ctx.arc(150, 25, 10, 0, Math.PI * 2);
ctx.fillStyle = '#ff0000';
ctx.fill();
return canvas.toDataURL('image/png');
}
WebGL 指纹采集
WebGL 指纹利用 GPU 渲染差异生成唯一标识。
function getWebGLFingerprint() {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) return null;
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
return {
vendor: gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL || gl.VENDOR),
renderer: gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL || gl.RENDERER),
unmaskedVendor: debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : null,
unmaskedRenderer: debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : null,
aliasedLineWidthRange: gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE),
aliasedPointSizeRange: gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE),
alphaBits: gl.getParameter(gl.ALPHA_BITS),
blueBits: gl.getParameter(gl.BLUE_BITS),
depthBits: gl.getParameter(gl.DEPTH_BITS),
maxCombinedTextureImageUnits: gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS),
maxCubeMapTextureSize: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),
maxFragmentUniformVectors: gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS),
maxRenderBufferSize: gl.getParameter(gl.MAX_RENDER_BUFFER_SIZE),
maxTextureImageUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
maxVaryingVectors: gl.getParameter(gl.MAX_VARYING_VECTORS),
maxVertexAttribs: gl.getParameter(gl.MAX_VERTEX_ATTRIBS),
maxVertexTextureImageUnits: gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS),
maxVertexUniformVectors: gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS),
precisionFormats: gl.getParameter(gl.PRECISION_FORMATS),
extensions: gl.getSupportedExtensions()
};
}
字体列表检测
通过测量不同字体的文本宽度差异来判断系统安装字体。
function getFontFingerprint() {
const baseFonts = ['monospace', 'sans-serif', 'serif'];
const testFonts = [
'Arial', 'Courier New', 'Georgia', 'Times New Roman',
'Verdana', 'Helvetica', 'Tahoma', 'Trebuchet MS'
];
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 100;
canvas.height = 20;
function getTextWidth(font) {
ctx.font = '16px ' + font;
return ctx.measureText('mmmmmmmmlli').width;
}
const baseWidth = baseFonts.map(f => getTextWidth(f));
const detected = [];
testFonts.forEach(font => {
baseFonts.forEach((base, i) => {
const width = getTextWidth(font + ',' + base);
if (width !== baseWidth[i]) {
detected.push(font);
}
});
});
return [...new Set(detected)];
}
综合指纹生成
将多个参数组合生成最终指纹标识。
async function getFullFingerprint() {
const basic = getBasicFingerprint();
const canvas = getCanvasFingerprint();
const webgl = getWebGLFingerprint();
const fonts = getFontFingerprint();
const fingerprint = {
...basic,
canvasHash: canvas ? await hashString(canvas) : null,
webglVendor: webgl?.unmaskedVendor || webgl?.vendor,
webglRenderer: webgl?.unmaskedRenderer || webgl?.renderer,
fonts: fonts,
timestamp: new Date().toISOString()
};
return fingerprint;
}
// 简单的字符串哈希函数
async function hashString(str) {
const encoder = new TextEncoder();
const data = encoder.encode(str);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
风险边界
- 采集不等于防护:获取指纹参数只是第一步,真正的防关联需要修改和隔离这些参数
- 采集行为可被检测:部分网站会检测 fingerprinting 行为并将其视为异常
- 不同浏览器差异大:同一设备在不同浏览器上的指纹可能完全不同
- 指纹会变化:系统更新、驱动更新、浏览器升级都可能导致指纹变化
- 法律合规:采集用户指纹信息需遵守相关隐私法规,建议在隐私政策中明确说明
与 EasyBR 的关系
EasyBR 指纹浏览器的核心能力正是修改上述所有参数,为每个环境生成独立且一致的指纹配置。开发者在理解指纹采集原理后,可以更好地配置和使用指纹浏览器进行多账号管理。
了解浏览器指纹包含哪些参数 | 查看 EasyBR 指纹浏览器原理
FAQ
Q: JS 获取的指纹会随时间变化吗?
A: 会。浏览器升级、系统更新、显卡驱动更新、安装新字体等都会导致指纹变化。稳定性较高的指纹组合是 Canvas + WebGL + 字体列表。
Q: 指纹采集会影响页面性能吗?
A: 基础参数获取几乎没有性能开销。Canvas 和 WebGL 指纹采集需要创建离屏画布,开销很小,建议在页面加载完成后异步执行。
Q: 普通用户能阻止指纹采集吗?
A: 部分可以。使用隐私模式、禁用 JavaScript、安装反追踪插件(如 Privacy Badger)可以减少指纹暴露,但也会影响正常网站功能。
Q: 移动端和桌面端的指纹有什么区别?
A: 移动端通常缺少 WebGL 扩展信息,字体列表更有限,屏幕分辨率差异更大,但整体指纹唯一性仍然很高。
Q: 指纹的唯一性有多高?
A: 根据 Panopticlick 研究,仅基础环境参数就能识别约 90% 的浏览器。加入 Canvas 和 WebGL 后,唯一性可超过 99%。
延伸阅读
先试 EasyBR,再决定是否扩团队或做更深的定制项目
标准版适合先验证多账号环境、代理和数据迁移;如果你需要更深的业务能力,我们也支持浏览器外包、Chromium 定制、贴牌浏览器与 Android 指纹浏览器开发。