/** * 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(); } })();