UNPKG

@ahmedelsharkawycs/browser-fingerprinting

Version:

A powerful, privacy-focused browser fingerprinting library that generates unique identifiers based on browser and device characteristics. Perfect for fraud prevention, analytics, and user identification without cookies.

158 lines (157 loc) 6.23 kB
const navigatorWithExtensions = navigator; function getCanvasFingerprint() { try { const canvas = document.createElement("canvas"); canvas.width = 200; canvas.height = 50; const ctx = canvas.getContext("2d"); if (ctx) { ctx.textBaseline = "top"; ctx.font = "14px Arial"; ctx.fillStyle = "#f60"; ctx.fillRect(0, 0, 100, 50); ctx.fillStyle = "#069"; ctx.fillText("BrowserFingerprint", 2, 15); return canvas.toDataURL(); } } catch (_) { } return "unknown"; } function getBrowserFingerprint() { const { userAgent, platform, vendor = "", language, languages = [], hardwareConcurrency = "unknown", deviceMemory = "unknown", plugins = [], mimeTypes = [], maxTouchPoints = 0, connection = {}, doNotTrack = "", cookieEnabled } = navigatorWithExtensions; const screen = { width: window.screen.width, height: window.screen.height, availWidth: window.screen.availWidth, availHeight: window.screen.availHeight, colorDepth: window.screen.colorDepth, pixelDepth: window.screen.pixelDepth, devicePixelRatio: window.devicePixelRatio || 1 }; const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const canvasFingerprint = getCanvasFingerprint(); const fonts = ["Arial", "Times New Roman", "Courier New", "Comic Sans MS", "Verdana", "Georgia"]; const fontCheck = {}; fonts.forEach((font) => { fontCheck[font] = document.fonts ? document.fonts.check(`12px "${font}"`) : false; }); let webglVendor = "unknown", webglRenderer = "unknown"; try { const canvas = document.createElement("canvas"); const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); if (gl) { const debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); if (debugInfo) { webglVendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL); webglRenderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); } } } catch (_) { } const touchSupport = { maxTouchPoints, touchEvent: "ontouchstart" in window, pointerEvent: "onpointerdown" in window }; const networkInfo = { effectiveType: connection.effectiveType, downlink: connection.downlink, rtt: connection.rtt }; const storage = { localStorage: !!window.localStorage, sessionStorage: !!window.sessionStorage, indexedDB: !!window.indexedDB, cookies: cookieEnabled }; // Remove dynamic performance benchmark that changes on each call const performanceBenchmark = { timing: 0, // Static value instead of performance.now() mathRandom: 0.123456789, // Static value instead of Math.random() mathSin: Math.sin(1) // This is deterministic, keep it }; const quirks = { chrome: !!window.chrome, edge: userAgent.includes("Edg/"), brave: userAgent.includes("Brave"), vivaldi: userAgent.includes("Vivaldi"), opera: userAgent.includes("OPR/") || userAgent.includes("Opera"), mobile: /Mobi|Android/i.test(userAgent), tablet: /Tablet|iPad/i.test(userAgent), desktop: !(/Mobi|Android/i.test(userAgent) || /Tablet|iPad/i.test(userAgent)), mac: platform.includes("Mac"), windows: platform.includes("Win"), linux: platform.includes("Linux"), android: /Android/i.test(userAgent), ios: /iPhone|iPad|iPod/i.test(userAgent), firefox: "InstallTrigger" in window, safari: /^((?!chrome|android).)*safari/i.test(userAgent), ie: !!document.documentMode }; return { userAgent, platform, vendor, screen, timezone, language, languages: languages, hardwareConcurrency, deviceMemory, canvasFingerprint, fontCheck, webgl: { vendor: webglVendor, renderer: webglRenderer }, plugins: Array.from(plugins).map((p) => ({ name: p.name, filename: p.filename, description: p.description })), mimeTypes: Array.from(mimeTypes).map((mt) => ({ type: mt.type, description: mt.description })), touchSupport, networkInfo, storage, performanceBenchmark, localIPs: [], // Static empty array instead of dynamic IPs doNotTrack, quirks }; } function normalizeFingerprint(f) { return { userAgent: f.userAgent, platform: f.platform, screen: { width: f.screen.width, height: f.screen.height, devicePixelRatio: f.screen.devicePixelRatio }, timezone: f.timezone, deviceMemory: f.deviceMemory, hardwareConcurrency: f.hardwareConcurrency, fonts: f.fontCheck, language: f.language, touchSupport: f.touchSupport, webglRenderer: f.webgl.renderer, webglVendor: f.webgl.vendor, plugins: f.plugins.map((p) => p.name).sort(), mimeTypes: f.mimeTypes.map((mt) => mt.type).sort() }; } function stringifyFingerprint(f) { return JSON.stringify(f); } function getFingerprintBase64(fingerprint, isNormalized = true) { const str = isNormalized ? stringifyFingerprint(fingerprint) : stringifyFingerprint(normalizeFingerprint(fingerprint)); const encoded = btoa(str); return encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } async function getFingerprintHashAsync(fingerprint, isNormalized = true) { const str = isNormalized ? stringifyFingerprint(fingerprint) : stringifyFingerprint(normalizeFingerprint(fingerprint)); const encoder = new TextEncoder(); const data = encoder.encode(str); const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); } export { getBrowserFingerprint, normalizeFingerprint, stringifyFingerprint, getFingerprintBase64, getFingerprintHashAsync };