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
JavaScript
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');
}