@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
JavaScript
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 };