UNPKG

browser-canvas-fingerprinting

Version:

A simple canvas fingerprinting implementation in browser with specific information used to generate fingerprint

86 lines (79 loc) 3.05 kB
import data from './data.js'; import { draw2d, drawText } from './draw2d.js'; import { asha256, bsha256, sha256 } from './sha256.js'; import { getGradients } from './effects.js'; import { urand } from './util.js'; async function drawContent(canvas, ctx) { const info = await data(); if (info.length < getGradients().length) info.push('Warning:: Data length less than effect length!') // 基本绘制 await draw2d(ctx, canvas); // 绘制文本信息 await drawText(canvas, ctx, info); } /** @returns {HTMLCanvasElement} */ export async function drawDirectly() { const canvas = document.createElement('canvas'); canvas.width = 1920; canvas.height = 1080; const ctx = canvas.getContext('2d'); await drawContent(canvas, ctx); return canvas; } /* -------- */ import getFonts from './font.js'; import { getFontsByCanvasMeasure } from './font.js'; import { getWebGLInfo, createWebGLFingerprint } from './webgl.js'; import { keyCount } from './util.js'; import { isExtensionFuckingCanvas } from './detection.js'; async function workerCode() { const canvas = new OffscreenCanvas(1920, 1080); const ctx = canvas.getContext('2d'); await drawContent(canvas, ctx); return await canvas.convertToBlob(); } function workerMain(str) { addEventListener('message', async e => postMessage({ $: str, _: await bsha256(await workerCode()) })); } /** @returns {string} the hash value */ export async function drawWithWorker() { if (!globalThis.OffscreenCanvas) throw new Error('OffscreenCanvas is not supported'); let url = null, w = null; try { // 优先尝试 Worker, 在 Worker 环境中几乎不会被干扰 const randStr = urand(2048).toString(); const deps = `${data}${asha256}${bsha256}${sha256}${getFonts}${getFontsByCanvasMeasure}${getWebGLInfo}${createWebGLFingerprint}${keyCount}${drawContent}${draw2d}${drawText}${getGradients}${isExtensionFuckingCanvas}${workerCode}` + `;globalThis.screen=({width:${screen.width},height:${screen.height},availWidth:${screen.availWidth},availHeight:${screen.availHeight}});`; const WorkerCode = `${deps};((${workerMain}))('${randStr}');`; // 尝试构造 Worker const bWorker = new Blob([WorkerCode], { type: 'text/javascript' }); url = URL.createObjectURL(bWorker); const hash = await new Promise((r, x) => { setTimeout(x, 5000); function handler(ev) { if (!(ev.data && ev.data.$ === randStr)) return; r(ev.data._); if (w) w.terminate(); // 避免资源浪费 } w = new Worker(url); w.onerror = x; w.onmessage = handler; w.postMessage({}); }); URL.revokeObjectURL(url); if (hash) return hash; } catch { // e.g. CSP/Jshelter 回退到直接获取法 if (url) URL.revokeObjectURL(url); if (w) w.terminate(); } throw new Error('Failed to use Worker'); }