browser-canvas-fingerprinting
Version:
A simple canvas fingerprinting implementation in browser with specific information used to generate fingerprint
130 lines (109 loc) • 4.3 kB
JavaScript
import { sha256, bsha256 } from './sha256.js';
function IsWebGLDataFucked(vendor) {
const knownLegitimateVendors = [
/intel/i,
/nvidia/i,
/amd/i,
/google/i,
/apple/i,
/microsoft/i,
/mesa/i,
/llvmpipe/i,
/vmware/i,
/qualcomm/i,
/arm/i,
/ati technologies/i,
/d3d/i
];
if (knownLegitimateVendors.some(pattern => pattern.test(vendor))) return false;
// JShelter
if (/^[A-Za-z0-9\-]{8}$/.test(vendor)) return true;
return false;
}
export function getWebGLInfo() {
try {
const canvas = new OffscreenCanvas(100, 100) || document.createElement('canvas');
const gl = canvas.getContext('webgl');
if (!gl) {
return {
vendor: 'N/A',
renderer: 'N/A'
};
}
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
const data = {
vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || 'N/A',
renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'N/A'
};
if (IsWebGLDataFucked(data.vendor) || IsWebGLDataFucked(data.renderer)) return {
vendor: 'Fucked by extension', renderer: 'Fucked by extension'
};
return data;
}
} catch {}
return {
vendor: 'N/A',
renderer: 'N/A'
};
}
export async function createWebGLFingerprint() {
try {
// 1. 创建 canvas 元素
const canvas = new OffscreenCanvas(512, 512) || document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const gl = canvas.getContext('webgl');
if (!gl) {
return 'Not Supported';
}
// 2. 定义顶点着色器源码
const vsSource = `attribute vec4 aPosition;void main() {gl_Position = aPosition;}`;
// 3. 定义片段着色器源码
const fsSource = `void main() {gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0); }`;
// 4. 创建着色器程序
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program));
return null;
}
gl.useProgram(program);
// 5. 创建顶点数据(一个三角形)
const positions = [
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5
];
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// 6. 连接着色器中的 attribute 到顶点数据
const positionLocation = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// 7. 清除背景并绘制
gl.clearColor(0.1, 0.2, 0.3, 1.0); // 深蓝色背景
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3); // 绘制三角形
// 8. 将 canvas 转为 data URL 并返回
if (canvas.convertToBlob) return await bsha256(await canvas.convertToBlob());
return await sha256(canvas.toDataURL('image/png'));
}
catch (e) { return 'Failed to generate: ' + String(e) }
}