/**
* Client-Side Fingerprinting for Fraud Detection
*
* This script collects browser fingerprint and behavior data to enhance
* fraud detection. Users can add this script to their landing pages.
*
* Usage:
*
*
* Or with custom configuration:
*
*
*/
(function() {
'use strict';
// Configuration with defaults
var config = window.TrackerFPConfig || {};
var endpoint = config.endpoint || (window.location.protocol + '//' + (config.trackerDomain || 'tracker.cybkit.com') + '/fingerprint_endpoint.php');
var sendDelay = config.sendDelay || 3000;
var sendOnLeave = config.sendOnLeave !== false;
// State
var dataSent = false;
var pageLoadTime = Date.now();
// Behavior tracking counters
var behavior = {
mousemove: 0,
click: 0,
keydown: 0,
scroll: 0,
touchstart: 0,
timeOnPage: 0
};
/**
* Get click_id from URL parameters
*/
function getClickId() {
var params = new URLSearchParams(window.location.search);
return params.get('click_id') || params.get('clickid') || params.get('cid') || null;
}
/**
* Collect screen information
*/
function getScreenInfo() {
return {
screen: screen.width + 'x' + screen.height,
colorDepth: screen.colorDepth,
pixelRatio: window.devicePixelRatio || 1
};
}
/**
* Collect timezone information
*/
function getTimezoneInfo() {
var timezone = '';
try {
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
} catch (e) {
timezone = 'unknown';
}
return {
timezone: timezone,
timezoneOffset: new Date().getTimezoneOffset()
};
}
/**
* Collect browser properties
*/
function getBrowserInfo() {
var nav = navigator;
return {
language: nav.language || '',
languages: (nav.languages || []).join(','),
cookieEnabled: nav.cookieEnabled,
doNotTrack: nav.doNotTrack || 'unspecified'
};
}
/**
* Collect hardware indicators
*/
function getHardwareInfo() {
var nav = navigator;
return {
hardwareConcurrency: nav.hardwareConcurrency || 0,
deviceMemory: nav.deviceMemory || 0,
maxTouchPoints: nav.maxTouchPoints || 0
};
}
/**
* Collect bot detection signals
*/
function getBotSignals() {
var nav = navigator;
return {
webdriver: nav.webdriver || false,
plugins: nav.plugins ? nav.plugins.length : 0
};
}
/**
* Generate canvas fingerprint
*/
function getCanvasFingerprint() {
try {
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 50;
var ctx = canvas.getContext('2d');
if (!ctx) {
return 'no_context';
}
// Draw text with various styles
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(10, 10, 100, 30);
ctx.fillStyle = '#069';
ctx.fillText('Fingerprint', 15, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText('Canvas', 50, 25);
// Get data URL and return last 50 characters as hash
var dataUrl = canvas.toDataURL();
return dataUrl.slice(-50);
} catch (e) {
return 'error';
}
}
/**
* Get WebGL fingerprint
*/
function getWebGLFingerprint() {
try {
var canvas = document.createElement('canvas');
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
return { vendor: '', renderer: '' };
}
var debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (!debugInfo) {
return { vendor: 'unknown', renderer: 'unknown' };
}
return {
vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || '',
renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || ''
};
} catch (e) {
return { vendor: 'error', renderer: 'error' };
}
}
/**
* Collect all fingerprint data
*/
function collectFingerprint() {
var screen = getScreenInfo();
var timezone = getTimezoneInfo();
var browser = getBrowserInfo();
var hardware = getHardwareInfo();
var botSignals = getBotSignals();
var webgl = getWebGLFingerprint();
return {
screen: screen.screen,
colorDepth: screen.colorDepth,
pixelRatio: screen.pixelRatio,
timezone: timezone.timezone,
timezoneOffset: timezone.timezoneOffset,
language: browser.language,
languages: browser.languages,
cookieEnabled: browser.cookieEnabled,
doNotTrack: browser.doNotTrack,
hardwareConcurrency: hardware.hardwareConcurrency,
deviceMemory: hardware.deviceMemory,
maxTouchPoints: hardware.maxTouchPoints,
webdriver: botSignals.webdriver,
plugins: botSignals.plugins,
canvas: getCanvasFingerprint(),
webgl_vendor: webgl.vendor,
webgl_renderer: webgl.renderer
};
}
/**
* Send data to tracker endpoint
*/
function sendData() {
if (dataSent) {
return;
}
var clickId = getClickId();
if (!clickId) {
// No click_id, don't send anything
return;
}
dataSent = true;
// Calculate time on page
behavior.timeOnPage = Date.now() - pageLoadTime;
var payload = {
click_id: clickId,
fingerprint: collectFingerprint(),
behavior: behavior
};
var jsonData = JSON.stringify(payload);
// Try sendBeacon first (most reliable for page unload)
if (navigator.sendBeacon) {
try {
var blob = new Blob([jsonData], { type: 'application/json' });
navigator.sendBeacon(endpoint, blob);
return;
} catch (e) {
// Fall through to fetch
}
}
// Fallback to fetch with keepalive
try {
fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: jsonData,
keepalive: true,
mode: 'cors',
credentials: 'omit' // Don't send cookies to avoid CORS issues
}).catch(function() {
// Silently fail
});
} catch (e) {
// Silently fail
}
}
/**
* Set up behavior tracking
*/
function setupBehaviorTracking() {
// Throttle function to limit event frequency
function throttle(callback, limit) {
var waiting = false;
return function() {
if (!waiting) {
callback.apply(this, arguments);
waiting = true;
setTimeout(function() {
waiting = false;
}, limit);
}
};
}
// Mouse move tracking (throttled)
document.addEventListener('mousemove', throttle(function() {
behavior.mousemove++;
}, 100), { passive: true });
// Click tracking
document.addEventListener('click', function() {
behavior.click++;
}, { passive: true });
// Keyboard tracking
document.addEventListener('keydown', function() {
behavior.keydown++;
}, { passive: true });
// Scroll tracking (throttled)
document.addEventListener('scroll', throttle(function() {
behavior.scroll++;
}, 100), { passive: true });
// Touch tracking
document.addEventListener('touchstart', function() {
behavior.touchstart++;
}, { passive: true });
}
/**
* Set up send triggers
*/
function setupSendTriggers() {
// Send after delay
setTimeout(sendData, sendDelay);
if (sendOnLeave) {
// Send when page visibility changes (user switches tab)
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'hidden') {
sendData();
}
});
// Send before page unload
window.addEventListener('beforeunload', sendData);
// Also try pagehide for mobile browsers
window.addEventListener('pagehide', sendData);
}
}
/**
* Initialize fingerprinting
*/
function init() {
// Don't run if no click_id in URL
if (!getClickId()) {
return;
}
setupBehaviorTracking();
setupSendTriggers();
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();