""" Advanced Fingerprint Service - Erweiterte Browser Fingerprinting Implementation """ import random import json import logging import hashlib from typing import List, Optional, Dict, Any, Tuple from datetime import datetime, timedelta import uuid from domain.services.fingerprint_service import IFingerprintService from domain.entities.browser_fingerprint import ( BrowserFingerprint, CanvasNoise, WebRTCConfig, HardwareConfig, NavigatorProperties, StaticComponents ) from infrastructure.repositories.fingerprint_repository import FingerprintRepository logger = logging.getLogger("advanced_fingerprint_service") class FingerprintProfiles: """Vordefinierte realistische Fingerprint-Profile""" DESKTOP_PROFILES = [ { "name": "Windows Chrome User", "platform": "Win32", "hardware_concurrency": [4, 8, 16], "device_memory": [4, 8, 16], "screen_resolution": [(1920, 1080), (2560, 1440), (1366, 768)], "vendor": "Google Inc.", "renderer": ["ANGLE (Intel HD Graphics)", "ANGLE (NVIDIA GeForce GTX)", "ANGLE (AMD Radeon)"] }, { "name": "MacOS Safari User", "platform": "MacIntel", "hardware_concurrency": [4, 8, 12], "device_memory": [8, 16, 32], "screen_resolution": [(1440, 900), (2560, 1600), (5120, 2880)], "vendor": "Apple Inc.", "renderer": ["Apple M1", "Intel Iris", "AMD Radeon Pro"] } ] MOBILE_PROFILES = [ { "name": "Android Chrome", "platform": "Linux armv8l", "hardware_concurrency": [4, 6, 8], "device_memory": [3, 4, 6, 8], "screen_resolution": [(360, 740), (375, 812), (414, 896)], "vendor": "Google Inc.", "renderer": ["Adreno", "Mali", "PowerVR"] }, { "name": "iOS Safari", "platform": "iPhone", "hardware_concurrency": [2, 4, 6], "device_memory": [2, 3, 4], "screen_resolution": [(375, 667), (375, 812), (414, 896)], "vendor": "Apple Inc.", "renderer": ["Apple GPU"] } ] COMMON_FONTS = { "windows": [ "Arial", "Arial Black", "Comic Sans MS", "Courier New", "Georgia", "Impact", "Times New Roman", "Trebuchet MS", "Verdana", "Webdings", "Wingdings", "Calibri", "Cambria", "Consolas", "Segoe UI", "Tahoma" ], "mac": [ "Arial", "Arial Black", "Comic Sans MS", "Courier New", "Georgia", "Helvetica", "Helvetica Neue", "Times New Roman", "Trebuchet MS", "Verdana", "American Typewriter", "Avenir", "Baskerville", "Big Caslon", "Futura", "Geneva", "Gill Sans" ], "linux": [ "Arial", "Courier New", "Times New Roman", "DejaVu Sans", "DejaVu Serif", "DejaVu Sans Mono", "Liberation Sans", "Liberation Serif", "Ubuntu", "Droid Sans", "Noto Sans" ] } class AdvancedFingerprintService(IFingerprintService): """Erweiterte Fingerprint-Service Implementation""" def __init__(self, repository: FingerprintRepository = None): self.repository = repository or FingerprintRepository() self.profiles = FingerprintProfiles() self.fingerprint_cache = {} def generate_fingerprint(self, profile_type: Optional[str] = None, platform: Optional[str] = None, proxy_location: Optional[str] = None, account_id: Optional[str] = None) -> BrowserFingerprint: """Generiert einen realistischen Fingerprint""" # Wähle Profil-Typ if profile_type == "mobile": profile = random.choice(self.profiles.MOBILE_PROFILES) else: profile = random.choice(self.profiles.DESKTOP_PROFILES) # Canvas Noise Configuration canvas_noise = CanvasNoise( noise_level=random.uniform(0.01, 0.05), seed=random.randint(1000, 9999), algorithm=random.choice(["gaussian", "uniform", "perlin"]) ) # WebRTC Configuration webrtc_config = WebRTCConfig( enabled=random.choice([True, False]), ice_servers=["stun:stun.l.google.com:19302"] if random.random() > 0.5 else [], local_ip_mask=f"10.0.{random.randint(0, 255)}.x", disable_webrtc=random.random() < 0.3 # 30% haben WebRTC deaktiviert ) # Hardware Configuration hardware_config = HardwareConfig( hardware_concurrency=random.choice(profile["hardware_concurrency"]), device_memory=random.choice(profile["device_memory"]), max_touch_points=10 if "mobile" in profile["name"].lower() else 0, screen_resolution=random.choice(profile["screen_resolution"]), color_depth=random.choice([24, 32]), pixel_ratio=random.choice([1.0, 1.5, 2.0, 3.0]) ) # Navigator Properties languages = self._generate_language_list() navigator_props = NavigatorProperties( platform=profile["platform"], vendor=profile["vendor"], language=languages[0], languages=languages, do_not_track=random.choice(["1", "unspecified", None]) ) # User Agent generieren navigator_props.user_agent = self._generate_user_agent(profile, navigator_props) # Font List font_list = self._generate_font_list(profile["platform"]) # WebGL webgl_vendor = profile["vendor"] webgl_renderer = random.choice(profile["renderer"]) # Audio Context audio_base_latency = random.uniform(0.00, 0.02) audio_output_latency = random.uniform(0.00, 0.05) audio_sample_rate = random.choice([44100, 48000]) # Timezone - konsistent mit Proxy-Location timezone, offset = self._get_timezone_for_location(proxy_location) # Plugins (nur für Desktop) plugins = [] if "mobile" not in profile["name"].lower(): plugins = self._generate_plugins() # Generate rotation seed for account-bound fingerprints rotation_seed = None if account_id: rotation_seed = hashlib.sha256(f"{account_id}:{datetime.now().strftime('%Y%m')}".encode()).hexdigest()[:16] # Create static components for persistence static_components = StaticComponents( device_type="mobile" if "mobile" in profile["name"].lower() else "desktop", os_family=self._get_os_family(profile["platform"]), browser_family="chromium" if "Chrome" in navigator_props.user_agent else "safari", gpu_vendor=webgl_vendor, gpu_model=webgl_renderer, cpu_architecture="arm64" if "arm" in profile["platform"].lower() else "x86_64", base_fonts=font_list[:10], # Store base fonts base_resolution=hardware_config.screen_resolution, base_timezone=timezone ) fingerprint = BrowserFingerprint( fingerprint_id=str(uuid.uuid4()), canvas_noise=canvas_noise, webrtc_config=webrtc_config, font_list=font_list, hardware_config=hardware_config, navigator_props=navigator_props, webgl_vendor=webgl_vendor, webgl_renderer=webgl_renderer, audio_context_base_latency=audio_base_latency, audio_context_output_latency=audio_output_latency, audio_context_sample_rate=audio_sample_rate, timezone=timezone, timezone_offset=offset, plugins=plugins, created_at=datetime.now(), # New persistence fields static_components=static_components if account_id else None, rotation_seed=rotation_seed, account_bound=bool(account_id) ) # Speichere in Repository self.repository.save(fingerprint) # Cache für schnellen Zugriff self.fingerprint_cache[fingerprint.fingerprint_id] = fingerprint logger.info(f"Generated new fingerprint: {fingerprint.fingerprint_id}") return fingerprint def _get_os_family(self, platform: str) -> str: """Determine OS family from platform string""" if "Win" in platform: return "windows" elif "Mac" in platform or "iPhone" in platform: return "macos" if "Mac" in platform else "ios" elif "Android" in platform or "Linux" in platform: return "android" if "Android" in platform else "linux" return "unknown" def _generate_language_list(self) -> List[str]: """Generiert realistische Sprachliste""" language_sets = [ ["de-DE", "de", "en-US", "en"], ["en-US", "en"], ["en-GB", "en-US", "en"], ["fr-FR", "fr", "en-US", "en"], ["es-ES", "es", "en-US", "en"], ["de-DE", "de", "en-GB", "en"] ] return random.choice(language_sets) def _generate_user_agent(self, profile: Dict, nav_props: NavigatorProperties) -> str: """Generiert realistischen User Agent""" chrome_version = random.randint(96, 120) if "Windows" in profile["name"]: return f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{chrome_version}.0.0.0 Safari/537.36" elif "Mac" in profile["name"]: return f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{chrome_version}.0.0.0 Safari/537.36" elif "Android" in profile["name"]: android_version = random.randint(10, 13) return f"Mozilla/5.0 (Linux; Android {android_version}; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{chrome_version}.0.0.0 Mobile Safari/537.36" elif "iOS" in profile["name"]: ios_version = random.randint(14, 16) return f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version}_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/{ios_version}.0 Mobile/15E148 Safari/604.1" return f"Mozilla/5.0 (compatible; Unknown)" def _generate_font_list(self, platform: str) -> List[str]: """Generiert plattform-spezifische Fontliste""" if "Win" in platform: fonts = self.profiles.COMMON_FONTS["windows"] elif "Mac" in platform or "iPhone" in platform: fonts = self.profiles.COMMON_FONTS["mac"] else: fonts = self.profiles.COMMON_FONTS["linux"] # Zufällige Auswahl (60-90% der Fonts) num_fonts = random.randint(int(len(fonts) * 0.6), int(len(fonts) * 0.9)) return random.sample(fonts, num_fonts) def _generate_plugins(self) -> List[Dict[str, str]]: """Generiert Plugin-Liste für Desktop""" all_plugins = [ {"name": "Chrome PDF Plugin", "filename": "internal-pdf-viewer"}, {"name": "Chrome PDF Viewer", "filename": "mhjfbmdgcfjbbpaeojofohoefgiehjai"}, {"name": "Native Client", "filename": "internal-nacl-plugin"}, {"name": "Shockwave Flash", "filename": "pepperflashplugin.dll"} ] # 0-3 Plugins num_plugins = random.randint(0, 3) return random.sample(all_plugins, num_plugins) def rotate_fingerprint(self, current: BrowserFingerprint, rotation_strategy: str = "gradual") -> BrowserFingerprint: """Rotiert einen Fingerprint""" if rotation_strategy == "complete": # Komplett neuer Fingerprint return self.generate_fingerprint() elif rotation_strategy == "minimal": # Nur kleine Änderungen new_fingerprint = BrowserFingerprint( fingerprint_id=str(uuid.uuid4()), canvas_noise=CanvasNoise( noise_level=current.canvas_noise.noise_level + random.uniform(-0.01, 0.01), seed=random.randint(1000, 9999), algorithm=current.canvas_noise.algorithm ), webrtc_config=current.webrtc_config, font_list=current.font_list, hardware_config=current.hardware_config, navigator_props=current.navigator_props, webgl_vendor=current.webgl_vendor, webgl_renderer=current.webgl_renderer, audio_context_base_latency=current.audio_context_base_latency + random.uniform(-0.001, 0.001), audio_context_output_latency=current.audio_context_output_latency, audio_context_sample_rate=current.audio_context_sample_rate, timezone=current.timezone, timezone_offset=current.timezone_offset, plugins=current.plugins, created_at=datetime.now(), last_rotated=datetime.now() ) else: # gradual # Moderate Änderungen new_fingerprint = BrowserFingerprint( fingerprint_id=str(uuid.uuid4()), canvas_noise=CanvasNoise( noise_level=random.uniform(0.01, 0.05), seed=random.randint(1000, 9999), algorithm=random.choice(["gaussian", "uniform", "perlin"]) ), webrtc_config=WebRTCConfig( enabled=current.webrtc_config.enabled, ice_servers=current.webrtc_config.ice_servers, local_ip_mask=f"10.0.{random.randint(0, 255)}.x", disable_webrtc=current.webrtc_config.disable_webrtc ), font_list=self._slightly_modify_fonts(current.font_list), hardware_config=current.hardware_config, navigator_props=current.navigator_props, webgl_vendor=current.webgl_vendor, webgl_renderer=current.webgl_renderer, audio_context_base_latency=random.uniform(0.00, 0.02), audio_context_output_latency=random.uniform(0.00, 0.05), audio_context_sample_rate=current.audio_context_sample_rate, timezone=current.timezone, timezone_offset=current.timezone_offset, plugins=current.plugins, created_at=current.created_at, last_rotated=datetime.now() ) # Update last_rotated für alten Fingerprint self.repository.update_last_rotated(current.fingerprint_id, datetime.now()) # Speichere neuen Fingerprint self.repository.save(new_fingerprint) self.fingerprint_cache[new_fingerprint.fingerprint_id] = new_fingerprint logger.info(f"Rotated fingerprint {current.fingerprint_id} -> {new_fingerprint.fingerprint_id} (strategy: {rotation_strategy})") return new_fingerprint def _slightly_modify_fonts(self, fonts: List[str]) -> List[str]: """Modifiziert Fontliste leicht""" new_fonts = fonts.copy() # Füge 1-2 Fonts hinzu oder entferne if random.random() > 0.5 and len(new_fonts) > 5: # Entferne 1-2 Fonts for _ in range(random.randint(1, 2)): if new_fonts: new_fonts.pop(random.randint(0, len(new_fonts) - 1)) else: # Füge 1-2 Fonts hinzu additional_fonts = ["Consolas", "Monaco", "Menlo", "Ubuntu Mono"] for font in random.sample(additional_fonts, min(2, len(additional_fonts))): if font not in new_fonts: new_fonts.append(font) return new_fonts def validate_fingerprint(self, fingerprint: BrowserFingerprint) -> Tuple[bool, List[str]]: """Validiert einen Fingerprint""" issues = [] # Hardware Konsistenz if fingerprint.hardware_config.hardware_concurrency > fingerprint.hardware_config.device_memory * 2: issues.append("Hardware concurrency zu hoch für device memory") # Platform Konsistenz if "Win" in fingerprint.navigator_props.platform and "Mac" in fingerprint.webgl_renderer: issues.append("Windows platform mit Mac renderer inkonsistent") # Mobile Konsistenz is_mobile = "iPhone" in fingerprint.navigator_props.platform or "Android" in fingerprint.navigator_props.user_agent if is_mobile and fingerprint.hardware_config.max_touch_points == 0: issues.append("Mobile device ohne touch points") # Font Konsistenz if len(fingerprint.font_list) < 5: issues.append("Zu wenige Fonts für realistisches Profil") # WebRTC Konsistenz if fingerprint.webrtc_config.disable_webrtc and fingerprint.webrtc_config.ice_servers: issues.append("WebRTC deaktiviert aber ICE servers konfiguriert") return len(issues) == 0, issues def save_fingerprint(self, fingerprint: BrowserFingerprint) -> None: """Speichert einen Fingerprint""" self.repository.save(fingerprint) self.fingerprint_cache[fingerprint.fingerprint_id] = fingerprint def load_fingerprint(self, fingerprint_id: str) -> Optional[BrowserFingerprint]: """Lädt einen Fingerprint""" # Check cache first if fingerprint_id in self.fingerprint_cache: return self.fingerprint_cache[fingerprint_id] # Load from repository fingerprint = self.repository.find_by_id(fingerprint_id) if fingerprint: self.fingerprint_cache[fingerprint_id] = fingerprint return fingerprint def get_fingerprint_pool(self, count: int = 10, platform: Optional[str] = None) -> List[BrowserFingerprint]: """Holt einen Pool von Fingerprints""" # Hole existierende Fingerprints existing = self.repository.get_random_fingerprints(count // 2) # Generiere neue für Diversität new_count = count - len(existing) new_fingerprints = [] for _ in range(new_count): fp = self.generate_fingerprint(platform=platform) new_fingerprints.append(fp) return existing + new_fingerprints def _get_timezone_for_location(self, proxy_location: Optional[str] = None) -> Tuple[str, int]: """Gibt Timezone basierend auf Proxy-Location zurück""" # Location-basierte Timezones location_timezones = { # Deutschland "DE": ("Europe/Berlin", -60), # UTC+1 "de": ("Europe/Berlin", -60), "germany": ("Europe/Berlin", -60), "berlin": ("Europe/Berlin", -60), "frankfurt": ("Europe/Berlin", -60), "munich": ("Europe/Berlin", -60), # UK "GB": ("Europe/London", 0), # UTC+0 "gb": ("Europe/London", 0), "uk": ("Europe/London", 0), "london": ("Europe/London", 0), # Frankreich "FR": ("Europe/Paris", -60), # UTC+1 "fr": ("Europe/Paris", -60), "france": ("Europe/Paris", -60), "paris": ("Europe/Paris", -60), # USA Ostküste "US-NY": ("America/New_York", 300), # UTC-5 "us-east": ("America/New_York", 300), "new york": ("America/New_York", 300), "newyork": ("America/New_York", 300), # USA Westküste "US-CA": ("America/Los_Angeles", 480), # UTC-8 "us-west": ("America/Los_Angeles", 480), "los angeles": ("America/Los_Angeles", 480), "california": ("America/Los_Angeles", 480), # Spanien "ES": ("Europe/Madrid", -60), # UTC+1 "es": ("Europe/Madrid", -60), "spain": ("Europe/Madrid", -60), "madrid": ("Europe/Madrid", -60), # Italien "IT": ("Europe/Rome", -60), # UTC+1 "it": ("Europe/Rome", -60), "italy": ("Europe/Rome", -60), "rome": ("Europe/Rome", -60), # Niederlande "NL": ("Europe/Amsterdam", -60), # UTC+1 "nl": ("Europe/Amsterdam", -60), "netherlands": ("Europe/Amsterdam", -60), "amsterdam": ("Europe/Amsterdam", -60), # Kanada "CA": ("America/Toronto", 300), # UTC-5 "ca": ("America/Toronto", 300), "canada": ("America/Toronto", 300), "toronto": ("America/Toronto", 300), # Australien "AU": ("Australia/Sydney", -660), # UTC+11 "au": ("Australia/Sydney", -660), "australia": ("Australia/Sydney", -660), "sydney": ("Australia/Sydney", -660), } # Wenn Location angegeben, verwende passende Timezone if proxy_location: # Normalisiere Location (lowercase, entferne Leerzeichen) normalized_location = proxy_location.lower().strip() # Suche in Location-Map for key, timezone_data in location_timezones.items(): if key.lower() in normalized_location or normalized_location in key.lower(): logger.info(f"Using timezone {timezone_data[0]} for location '{proxy_location}'") return timezone_data # Fallback: Zufällige Timezone aus häufig genutzten common_timezones = [ ("Europe/Berlin", -60), ("Europe/London", 0), ("Europe/Paris", -60), ("America/New_York", 300), ("America/Los_Angeles", 480), ("Europe/Madrid", -60), ("America/Toronto", 300) ] timezone_data = random.choice(common_timezones) logger.info(f"Using random timezone {timezone_data[0]} (no location match for '{proxy_location}')") return timezone_data def apply_fingerprint(self, browser_context: Any, fingerprint: BrowserFingerprint) -> None: """Wendet Fingerprint auf Browser Context an""" # Diese Methode würde JavaScript injection und Browser-Konfiguration durchführen # Beispiel-Implementation für Playwright: if hasattr(browser_context, 'add_init_script'): # Canvas Noise Injection canvas_script = self._generate_canvas_noise_script(fingerprint.canvas_noise) browser_context.add_init_script(canvas_script) # WebRTC Protection if fingerprint.webrtc_config.disable_webrtc: webrtc_script = self._generate_webrtc_block_script() browser_context.add_init_script(webrtc_script) # Navigator Override navigator_script = self._generate_navigator_override_script(fingerprint.navigator_props) browser_context.add_init_script(navigator_script) # Hardware Override hardware_script = self._generate_hardware_override_script(fingerprint.hardware_config) browser_context.add_init_script(hardware_script) logger.info(f"Applied fingerprint {fingerprint.fingerprint_id} to browser context") def _generate_canvas_noise_script(self, canvas_noise: CanvasNoise) -> str: """Generiert Canvas Noise Injection Script""" return f""" (function() {{ const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData; const noiseLevel = {canvas_noise.noise_level}; const seed = {canvas_noise.seed}; CanvasRenderingContext2D.prototype.getImageData = function() {{ const imageData = originalGetImageData.apply(this, arguments); // Add noise to image data for (let i = 0; i < imageData.data.length; i += 4) {{ imageData.data[i] += Math.random() * noiseLevel * 255; imageData.data[i+1] += Math.random() * noiseLevel * 255; imageData.data[i+2] += Math.random() * noiseLevel * 255; }} return imageData; }}; }})(); """ def _generate_webrtc_block_script(self) -> str: """Generiert erweiterten WebRTC Block Script mit IP Leak Prevention""" return """ (function() { // Erweiterte WebRTC Leak Prevention // 1. Basis WebRTC Blocking const OriginalRTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; if (OriginalRTCPeerConnection) { // Override RTCPeerConnection window.RTCPeerConnection = function(config, constraints) { // Filtere ICE Server wenn gewünscht if (config && config.iceServers) { config.iceServers = config.iceServers.filter(server => { // Entferne STUN Server die IP leaken könnten if (server.urls) { const urls = Array.isArray(server.urls) ? server.urls : [server.urls]; return urls.every(url => !url.includes('stun:')); } return true; }); } const pc = new OriginalRTCPeerConnection(config, constraints); // Override onicecandidate const originalOnIceCandidate = pc.onicecandidate; Object.defineProperty(pc, 'onicecandidate', { get: function() { return originalOnIceCandidate; }, set: function(func) { originalOnIceCandidate = function(event) { if (event.candidate) { // Filtere lokale IP Adressen const candidateStr = event.candidate.candidate; // Regex für private IPs const privateIPRegex = /(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|[a-f0-9:]+::/gi); // Wenn private IP gefunden, modifiziere Kandidat if (privateIPRegex.test(candidateStr)) { const modifiedCandidate = candidateStr.replace(privateIPRegex, '10.0.0.x'); event.candidate.candidate = modifiedCandidate; } } if (func) { func(event); } }; } }); // Override createDataChannel const originalCreateDataChannel = pc.createDataChannel; pc.createDataChannel = function(label, options) { // Log für Debugging aber blockiere nicht console.debug('DataChannel created:', label); return originalCreateDataChannel.call(this, label, options); }; // Override getStats für Fingerprinting Protection const originalGetStats = pc.getStats; pc.getStats = function() { return originalGetStats.call(this).then(stats => { // Modifiziere Stats um Fingerprinting zu erschweren stats.forEach(stat => { if (stat.type === 'candidate-pair') { // Verstecke echte RTT if (stat.currentRoundTripTime) { stat.currentRoundTripTime = Math.random() * 0.1 + 0.05; } } }); return stats; }); }; return pc; }; // Kopiere statische Eigenschaften window.RTCPeerConnection.prototype = OriginalRTCPeerConnection.prototype; window.RTCPeerConnection.generateCertificate = OriginalRTCPeerConnection.generateCertificate; // Aliase für andere Browser if (window.webkitRTCPeerConnection) { window.webkitRTCPeerConnection = window.RTCPeerConnection; } if (window.mozRTCPeerConnection) { window.mozRTCPeerConnection = window.RTCPeerConnection; } } // 2. MediaDevices Protection if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { const originalEnumerateDevices = navigator.mediaDevices.enumerateDevices; navigator.mediaDevices.enumerateDevices = function() { return originalEnumerateDevices.call(this).then(devices => { // Randomisiere Device IDs return devices.map(device => { return { ...device, deviceId: device.deviceId ? btoa(Math.random().toString()).substring(0, 20) : '', groupId: device.groupId ? btoa(Math.random().toString()).substring(0, 20) : '' }; }); }); }; } // 3. Block WebRTC komplett wenn gewünscht if (window.__BLOCK_WEBRTC_COMPLETELY__) { delete window.RTCPeerConnection; delete window.webkitRTCPeerConnection; delete window.mozRTCPeerConnection; delete window.RTCSessionDescription; delete window.RTCIceCandidate; delete window.MediaStream; delete window.MediaStreamTrack; } })(); """ def _generate_navigator_override_script(self, nav_props: NavigatorProperties) -> str: """Generiert Navigator Override Script""" return f""" (function() {{ Object.defineProperty(navigator, 'platform', {{ get: () => '{nav_props.platform}' }}); Object.defineProperty(navigator, 'vendor', {{ get: () => '{nav_props.vendor}' }}); Object.defineProperty(navigator, 'language', {{ get: () => '{nav_props.language}' }}); Object.defineProperty(navigator, 'languages', {{ get: () => {json.dumps(nav_props.languages)} }}); }})(); """ def _generate_hardware_override_script(self, hw_config: HardwareConfig) -> str: """Generiert Hardware Override Script""" return f""" (function() {{ Object.defineProperty(navigator, 'hardwareConcurrency', {{ get: () => {hw_config.hardware_concurrency} }}); Object.defineProperty(navigator, 'deviceMemory', {{ get: () => {hw_config.device_memory} }}); Object.defineProperty(navigator, 'maxTouchPoints', {{ get: () => {hw_config.max_touch_points} }}); }})(); """ def get_fingerprint_score(self, fingerprint: BrowserFingerprint) -> float: """Bewertet Fingerprint-Qualität""" score = 1.0 # Validierung valid, issues = self.validate_fingerprint(fingerprint) if not valid: score -= 0.1 * len(issues) # Alter des Fingerprints age = datetime.now() - fingerprint.created_at if age > timedelta(days=7): score -= 0.2 elif age > timedelta(days=30): score -= 0.4 # Rotation if fingerprint.last_rotated: time_since_rotation = datetime.now() - fingerprint.last_rotated if time_since_rotation < timedelta(hours=1): score -= 0.3 # Zu häufige Rotation # Font-Diversität if len(fingerprint.font_list) < 10: score -= 0.1 elif len(fingerprint.font_list) > 50: score -= 0.1 # Zu viele Fonts unrealistisch return max(0.0, min(1.0, score)) def create_account_fingerprint(self, account_id: str, profile_type: Optional[str] = None, platform: Optional[str] = None, proxy_location: Optional[str] = None) -> BrowserFingerprint: """Creates a new fingerprint bound to a specific account""" fingerprint = self.generate_fingerprint( profile_type=profile_type, platform=platform, proxy_location=proxy_location, account_id=account_id ) # Link fingerprint to account self.repository.link_to_account(fingerprint.fingerprint_id, account_id) return fingerprint def get_account_fingerprint(self, account_id: str) -> Optional[BrowserFingerprint]: """Get the primary fingerprint for an account""" try: fingerprint_id = self.repository.get_primary_fingerprint_for_account(account_id) if fingerprint_id: logger.debug(f"Found fingerprint {fingerprint_id} for account {account_id}") return self.load_fingerprint(fingerprint_id) else: logger.debug(f"No fingerprint found for account {account_id}") return None except Exception as e: logger.error(f"Error getting fingerprint for account {account_id}: {e}") return None def load_for_session(self, fingerprint_id: str, date_str: Optional[str] = None) -> BrowserFingerprint: """Load fingerprint for a session with deterministic daily variations""" try: fingerprint = self.load_fingerprint(fingerprint_id) if not fingerprint: logger.error(f"Fingerprint {fingerprint_id} not found in repository") raise ValueError(f"Fingerprint {fingerprint_id} not found") logger.debug(f"Loading fingerprint {fingerprint_id} for session") except Exception as e: logger.error(f"Error loading fingerprint {fingerprint_id}: {e}") raise if not fingerprint.rotation_seed: # No seed means no deterministic variation return fingerprint # Apply deterministic variations based on date if date_str is None: date_str = datetime.now().strftime("%Y-%m-%d") # Create a copy with daily variations session_fingerprint = BrowserFingerprint.from_dict(fingerprint.to_dict()) # Apply deterministic noise to canvas seed hash_input = f"{fingerprint.rotation_seed}:canvas:{date_str}" canvas_seed = int(hashlib.sha256(hash_input.encode()).hexdigest()[:8], 16) % 1000000 session_fingerprint.canvas_noise.seed = canvas_seed # Slight audio variations hash_input = f"{fingerprint.rotation_seed}:audio:{date_str}" audio_var = int(hashlib.sha256(hash_input.encode()).hexdigest()[:8], 16) / 0xFFFFFFFF session_fingerprint.audio_context_base_latency += (audio_var - 0.5) * 0.002 return session_fingerprint def update_fingerprint_stats(self, fingerprint_id: str, account_id: str, success: bool) -> None: """Update fingerprint usage statistics for an account""" self.repository.update_fingerprint_stats(fingerprint_id, account_id, success) def cleanup_old_fingerprints(self, older_than: datetime) -> int: """Bereinigt alte Fingerprints - Dummy implementation""" # Removed functionality, just return 0 return 0 def detect_fingerprinting(self, page_content: str) -> Dict[str, Any]: """Erkennt Fingerprinting-Versuche - Dummy implementation""" # Removed functionality, return empty detection return { "canvas": False, "webrtc": False, "fonts": False, "audio": False, "webgl": False, "hardware": False, "techniques": [], "total_techniques": 0, "risk_level": "none" } def get_fingerprint_pool(self, count: int = 10, platform: Optional[str] = None) -> List[BrowserFingerprint]: """Holt einen Pool von Fingerprints - Simple implementation""" # Just generate new fingerprints fingerprints = [] for _ in range(count): fp = self.generate_fingerprint(platform=platform) fingerprints.append(fp) return fingerprints