thumb-browser
Version:
A blazing-fast, dependency-free browser fingerprinting library to uniquely identify devices using entropy data. Designed for high performance, privacy-respecting analytics, bot detection, and session tracking in modern web apps.
181 lines (180 loc) • 6.13 kB
JavaScript
var s = (u, t, e) => new Promise((a, r) => {
var i = (n) => {
try {
o(e.next(n));
} catch (g) {
r(g);
}
}, c = (n) => {
try {
o(e.throw(n));
} catch (g) {
r(g);
}
}, o = (n) => n.done ? a(n.value) : Promise.resolve(n.value).then(i, c);
o((e = e.apply(u, t)).next());
});
class d {
constructor() {
this.data = [];
}
/**
* Generates the unique fingerprint by collecting system/browser data
* and hashing it using SHA-256.
* @returns {Promise<string>} The generated fingerprint hash
*/
get() {
return s(this, null, function* () {
yield this.collect();
const t = this.data.join("||");
return yield this.hash(t);
});
}
/**
* Collects various browser/device characteristics to generate a fingerprint.
* This includes User Agent, screen resolution, canvas fingerprint, etc.
*/
collect() {
return s(this, null, function* () {
this.data = [
navigator.userAgent,
// User Agent string for browser identification
navigator.language,
// User language setting
screen.width + "x" + screen.height + "x" + screen.colorDepth,
// Screen resolution and color depth
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
// Timezone offset
this.getCanvas(),
// Canvas fingerprint for uniqueness
yield this.getAudio(),
// Audio context fingerprint (unique to device)
this.getWebGL(),
// WebGL fingerprint (unique renderer info)
navigator.maxTouchPoints,
// Number of touch points supported
navigator.deviceMemory || "unknown",
// Device memory (if available)
navigator.hardwareConcurrency || "unknown",
// Number of CPU cores
Intl.DateTimeFormat().resolvedOptions().timeZone,
// Timezone
navigator.doNotTrack,
// Do Not Track setting in the browser
`${window.innerWidth}x${window.innerHeight}`,
// Window size
this.getPlugins(),
// Installed browser plugins
this.getColorGamut(),
// Color gamut of the screen (P3 or sRGB)
this.getBattery(),
// Battery status (if available)
this.getNetwork(),
// Network connection type (4G, WiFi, etc.)
this.getNoise()
// Random noise for added entropy
];
});
}
/**
* Generates a fingerprint based on the canvas rendering result.
* This is unique to the specific device's GPU.
* @returns {string} A base64-encoded string of the canvas content
*/
getCanvas() {
try {
const t = document.createElement("canvas"), e = t.getContext("2d");
return e.textBaseline = "top", e.font = "14px Arial", e.fillStyle = "#f60", e.fillRect(0, 0, 100, 40), e.fillStyle = "#069", e.fillText("🧠 Fingerprint!", 2, 2), t.toDataURL();
} catch (t) {
return "canvas_unsupported";
}
}
/**
* Generates an audio fingerprint by rendering an audio context and measuring the output.
* @returns {Promise<string>} The audio fingerprint value (hash of the output)
*/
getAudio() {
return s(this, null, function* () {
try {
const t = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(1, 44100, 44100), e = t.createOscillator(), a = t.createGain();
return e.type = "triangle", e.frequency.value = 1e4, e.connect(a), a.connect(t.destination), e.start(0), t.startRendering(), new Promise((r) => {
t.oncomplete = (i) => {
const c = i.renderedBuffer.getChannelData(0);
let o = 0;
for (let n = 0; n < c.length; n++) o += Math.abs(c[n]);
r(o.toString());
};
});
} catch (t) {
return "audio_unsupported";
}
});
}
/**
* Collects WebGL information, specifically the vendor and renderer information,
* to uniquely identify the GPU.
* @returns {string} WebGL renderer information
*/
getWebGL() {
try {
const e = document.createElement("canvas").getContext("webgl"), a = e.getExtension("WEBGL_debug_renderer_info"), r = e.getParameter(a.UNMASKED_VENDOR_WEBGL), i = e.getParameter(a.UNMASKED_RENDERER_WEBGL);
return `${r}~${i}`;
} catch (t) {
return "webgl_unsupported";
}
}
/**
* Retrieves the list of installed browser plugins.
* @returns {string} A comma-separated list of plugin names
*/
getPlugins() {
try {
return navigator.plugins ? [...navigator.plugins].map((t) => t.name).join(",") : "no_plugins";
} catch (t) {
return "plugins_unsupported";
}
}
/**
* Determines the color gamut of the device (P3 or sRGB).
* @returns {string} The color gamut of the device
*/
getColorGamut() {
return window.matchMedia("(color-gamut: p3)").matches ? "p3" : "srgb";
}
/**
* Retrieves the battery level of the device (if accessible).
* @returns {Promise<string>} Battery level or 'battery_unsupported' if not accessible
*/
getBattery() {
return navigator.getBattery ? navigator.getBattery().then((t) => t.level) : "battery_unsupported";
}
/**
* Retrieves the network connection type (e.g., WiFi, 4G).
* @returns {string} Network connection type or 'network_unsupported'
*/
getNetwork() {
const t = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
return t ? t.effectiveType : "network_unsupported";
}
/**
* Adds a random noise string for added entropy in fingerprint generation.
* @returns {string} A random noise string
*/
getNoise() {
return Math.random().toString(36).substring(2, 15);
}
/**
* Generates a SHA-256 hash of the provided string.
* @param {string} str The string to hash
* @returns {Promise<string>} The SHA-256 hash of the string
*/
hash(t) {
return s(this, null, function* () {
const e = new TextEncoder().encode(t), a = yield crypto.subtle.digest("SHA-256", e);
return Array.from(new Uint8Array(a)).map((r) => r.toString(16).padStart(2, "0")).join("");
});
}
}
export {
d as default
};