UNPKG

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
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) } }