Untitled
unknown
plain_text
9 months ago
6.6 kB
12
Indexable
async function sha256Hex(str) {
const enc = new TextEncoder();
const data = enc.encode(str);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const bytes = new Uint8Array(hashBuffer);
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
}
function getOrCreateClientId() {
try {
const key = 'my_app_client_id_v1';
let id = localStorage.getItem(key);
if (!id) {
// simple UUID v4
id = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
localStorage.setItem(key, id);
}
return id;
} catch (e) {
return 'volatile-' + Math.random().toString(36).slice(2);
}
}
function getCanvasFingerprint() {
try {
const cvs = document.createElement('canvas');
cvs.width = 200;
cvs.height = 50;
const ctx = cvs.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = "16px 'Arial'";
ctx.fillStyle = '#f60';
ctx.fillRect(0, 0, 200, 50);
ctx.fillStyle = '#069';
ctx.fillText('Fingerprint test — åßç≈', 2, 2);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText('Fingerprint test — åßç≈', 4, 20);
const data = cvs.toDataURL();
return data;
} catch (e) {
return 'canvas-error';
}
}
function getWebGLFingerprint() {
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) return 'no-webgl';
const dbgRenderInfo = gl.getExtension('WEBGL_debug_renderer_info');
const vendor = dbgRenderInfo ? gl.getParameter(dbgRenderInfo.UNMASKED_VENDOR_WEBGL) : 'unknown';
const renderer = dbgRenderInfo ? gl.getParameter(dbgRenderInfo.UNMASKED_RENDERER_WEBGL) : 'unknown';
return vendor + '|' + renderer;
} catch (e) {
return 'webgl-error';
}
}
function getAudioFingerprint() {
try {
const AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
if (!AudioContext) return 'no-audio';
const ctx = new AudioContext(1, 44100, 44100);
const oscillator = ctx.createOscillator();
const analyser = ctx.createAnalyser();
oscillator.type = 'triangle';
oscillator.frequency.value = 10000;
oscillator.connect(analyser);
analyser.connect(ctx.destination);
if (ctx.startRendering) {
return new Promise((res) => {
try {
const offline = new AudioContext(1, 44100, 44100);
res('audio-' + (offline.sampleRate || 0) + '-' + (offline.numberOfChannels || 0));
} catch (e) {
res('audio-error');
}
});
} else {
return Promise.resolve('audio-basic-' + (ctx.sampleRate || 0));
}
} catch (e) {
return Promise.resolve('audio-error');
}
}
function probeFonts() {
try {
const baseFonts = ['monospace', 'sans-serif', 'serif'];
const testFonts = ['Arial', 'Times New Roman', 'Courier New', 'Segoe UI', 'Roboto', 'Noto Sans'];
const span = document.createElement('span');
span.style.fontSize = '72px';
span.innerHTML = 'mmmmmmmmmwwwwwww';
const defaultWidth = {};
document.body.appendChild(span);
baseFonts.forEach(bf => {
span.style.fontFamily = bf;
defaultWidth[bf] = span.getBoundingClientRect().width;
});
const found = [];
for (const f of testFonts) {
span.style.fontFamily = `${f}, monospace`;
const w = span.getBoundingClientRect().width;
if (w !== defaultWidth['monospace']) found.push(f);
}
document.body.removeChild(span);
return found.join(',');
} catch (e) {
return 'font-probe-error';
}
}
async function generateFingerprintEnhanced() {
const nav = window.navigator || {};
const screenInfo = window.screen || {};
const components = [
nav.userAgent || '',
nav.vendor || '',
navigator.language || '',
navigator.languages ? navigator.languages.join(',') : '',
screenInfo.width + 'x' + screenInfo.height + 'x' + (window.devicePixelRatio || 1),
'colorDepth:' + (screenInfo.colorDepth || 0),
'pixelDepth:' + (screenInfo.pixelDepth || 0),
'platform:' + (nav.platform || ''),
'hardwareConcurrency:' + (nav.hardwareConcurrency || ''),
'deviceMemory:' + (navigator.deviceMemory || ''),
'touchPoints:' + (navigator.maxTouchPoints || navigator.msMaxTouchPoints || 0),
'cookieEnabled:' + (navigator.cookieEnabled ? 1 : 0),
'doNotTrack:' + (navigator.doNotTrack || ''),
'timezoneOffset:' + new Date().getTimezoneOffset(),
'timezoneName:' + (Intl.DateTimeFormat && Intl.DateTimeFormat().resolvedOptions().timeZone ? Intl.DateTimeFormat().resolvedOptions().timeZone : ''),
'plugins:' + (navigator.plugins ? Array.from(navigator.plugins).map(p => p.name + '/' + p.filename).join(';') : ''),
'mimeTypes:' + (navigator.mimeTypes ? Array.from(navigator.mimeTypes).map(m => m.type).join(',') : '')
];
components.push('canvas:' + getCanvasFingerprint());
components.push('webgl:' + getWebGLFingerprint());
try {
const fonts = probeFonts();
components.push('fonts:' + fonts);
} catch (e) {
components.push('fonts:error');
}
try {
const audioFp = await getAudioFingerprint();
components.push('audio:' + audioFp);
} catch (e) {
components.push('audio:error');
}
try {
if (performance && performance.timing) {
components.push('pt:' + (performance.timing.navigationStart || 0));
}
} catch (e) {
}
const seed = (nav.userAgent || '') + '|' + (nav.vendor || '') + '|' + (navigator.language || '');
const seedHash = await sha256Hex(seed);
components.push('seed:' + seedHash.slice(0, 8));
const canon = components.join('||');
const fingerprint = await sha256Hex(canon);
const clientId = getOrCreateClientId();
return {fingerprint, clientId, raw: canon};
}
Editor is loading...
Leave a Comment