Major Security Improvements:

- Enhanced user fingerprinting with WebGL, Canvas, and Audio fingerprinting
- Hardware binding to prevent F5/Ctrl+F5 abuse
- Persistent storage across browser sessions (localStorage + sessionStorage)
- Global demo session counter with 10 session limit per device
- Multi-tab protection (max 2 tabs simultaneously)
- Anti-reset protection with hardware mismatch detection

Demo Session Protection:
- Advanced fingerprint generation with CPU benchmarking
- Enhanced validation with cryptographic verification
- Automatic cleanup and session completion tracking
- Cooldown periods between sessions (1min + 15min completion)
- Weekly partial reset of global counters

Fixes:
- Fixed SessionTimer console spam after connection disconnect
- Added missing registerEnhancedDemoSessionUsage method
- Corrected method calls from generateUserFingerprint to generateAdvancedUserFingerprint
- Implemented proper event handling for connection state changes

WebRTC Improvements:
- Added peer-disconnect, new-connection, and connection-cleaned events
- Enhanced connection cleanup with proper UI notifications
- Fixed SessionTimer state management during disconnections
- Prevented infinite re-rendering and console logging

Performance Optimizations:
- Auto-save persistent data every 30 seconds
- Periodic cleanup of old session data (every 6 hours)
- Memory management for used preimages (10k limit)
- Tab heartbeat system for multi-tab detection

Testing:
- Demo sessions now properly enforce limits
- P2P anonymity maintained (no server validation)
- Compatible with incognito mode restrictions
- Resistant to common abuse techniques
This commit is contained in:
lockbitchat
2025-08-16 20:58:42 -04:00
parent 32635839c6
commit e4273f5150
5 changed files with 697 additions and 136 deletions

View File

@@ -3244,7 +3244,6 @@
} }
try { try {
console.log('📤 Attempting to send message:', messageInput.substring(0, 100));
// Add the message to local messages immediately (sent message) // Add the message to local messages immediately (sent message)
const sentMessage = { const sentMessage = {

View File

@@ -163,7 +163,7 @@ const EnhancedMinimalHeader = ({
React.createElement('p', { React.createElement('p', {
key: 'subtitle', key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block' className: 'text-xs sm:text-sm text-muted hidden sm:block'
}, 'End-to-end freedom. v4.0.02.88') }, 'End-to-end freedom. v4.0.03.00')
]) ])
]), ]),

View File

@@ -2,12 +2,17 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
const [currentTime, setCurrentTime] = React.useState(timeLeft || 0); const [currentTime, setCurrentTime] = React.useState(timeLeft || 0);
const [showExpiredMessage, setShowExpiredMessage] = React.useState(false); const [showExpiredMessage, setShowExpiredMessage] = React.useState(false);
const [initialized, setInitialized] = React.useState(false); const [initialized, setInitialized] = React.useState(false);
const [connectionBroken, setConnectionBroken] = React.useState(false); const [connectionBroken, setConnectionBroken] = React.useState(false);
const [loggedHidden, setLoggedHidden] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
if (connectionBroken) { if (connectionBroken) {
console.log('⏱️ SessionTimer initialization skipped - connection broken'); if (!loggedHidden) {
console.log('⏱️ SessionTimer initialization skipped - connection broken');
setLoggedHidden(true);
}
return; return;
} }
@@ -23,17 +28,22 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
setCurrentTime(initialTime); setCurrentTime(initialTime);
setInitialized(true); setInitialized(true);
}, [sessionManager, connectionBroken]); setLoggedHidden(false);
}, [sessionManager, connectionBroken]);
React.useEffect(() => { React.useEffect(() => {
if (connectionBroken) { if (connectionBroken) {
console.log('⏱️ SessionTimer props update skipped - connection broken'); if (!loggedHidden) {
console.log('⏱️ SessionTimer props update skipped - connection broken');
setLoggedHidden(true);
}
return; return;
} }
if (timeLeft && timeLeft > 0) { if (timeLeft && timeLeft > 0) {
setCurrentTime(timeLeft); setCurrentTime(timeLeft);
} }
setLoggedHidden(false);
}, [timeLeft, connectionBroken]); }, [timeLeft, connectionBroken]);
React.useEffect(() => { React.useEffect(() => {
@@ -42,7 +52,10 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
} }
if (connectionBroken) { if (connectionBroken) {
console.log('⏱️ Timer interval skipped - connection broken'); if (!loggedHidden) {
console.log('⏱️ Timer interval skipped - connection broken');
setLoggedHidden(true);
}
return; return;
} }
@@ -50,7 +63,6 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
return; return;
} }
const interval = setInterval(() => { const interval = setInterval(() => {
if (connectionBroken) { if (connectionBroken) {
console.log('⏱️ Timer interval stopped - connection broken'); console.log('⏱️ Timer interval stopped - connection broken');
@@ -81,22 +93,18 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
}, 1000); }, 1000);
return () => { return () => {
clearInterval(interval); clearInterval(interval);
}; };
}, [initialized, currentTime, sessionManager, connectionBroken]); }, [initialized, currentTime, sessionManager, connectionBroken]);
React.useEffect(() => { React.useEffect(() => {
const handleSessionTimerUpdate = (event) => { const handleSessionTimerUpdate = (event) => {
if (event.detail.timeLeft && event.detail.timeLeft > 0) { if (event.detail.timeLeft && event.detail.timeLeft > 0) {
setCurrentTime(event.detail.timeLeft); setCurrentTime(event.detail.timeLeft);
} }
}; };
const handleForceHeaderUpdate = (event) => { const handleForceHeaderUpdate = (event) => {
if (sessionManager && sessionManager.hasActiveSession()) { if (sessionManager && sessionManager.hasActiveSession()) {
const newTime = sessionManager.getTimeLeft(); const newTime = sessionManager.getTimeLeft();
setCurrentTime(newTime); setCurrentTime(newTime);
@@ -105,28 +113,41 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
const handlePeerDisconnect = (event) => { const handlePeerDisconnect = (event) => {
console.log('🔌 Peer disconnect detected in SessionTimer - stopping timer permanently'); console.log('🔌 Peer disconnect detected in SessionTimer - stopping timer permanently');
setConnectionBroken(true); setConnectionBroken(true);
setCurrentTime(0); setCurrentTime(0);
setShowExpiredMessage(false); setShowExpiredMessage(false);
setLoggedHidden(false);
}; };
const handleNewConnection = (event) => { const handleNewConnection = (event) => {
console.log('🔌 New connection detected in SessionTimer - resetting connection state'); console.log('🔌 New connection detected in SessionTimer - resetting connection state');
setConnectionBroken(false); setConnectionBroken(false);
setLoggedHidden(false);
};
const handleConnectionCleaned = (event) => {
console.log('🧹 Connection cleaned - resetting SessionTimer state');
setConnectionBroken(false);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
}; };
document.addEventListener('session-timer-update', handleSessionTimerUpdate); document.addEventListener('session-timer-update', handleSessionTimerUpdate);
document.addEventListener('force-header-update', handleForceHeaderUpdate); document.addEventListener('force-header-update', handleForceHeaderUpdate);
document.addEventListener('peer-disconnect', handlePeerDisconnect); document.addEventListener('peer-disconnect', handlePeerDisconnect);
document.addEventListener('new-connection', handleNewConnection); document.addEventListener('new-connection', handleNewConnection);
document.addEventListener('connection-cleaned', handleConnectionCleaned);
return () => { return () => {
document.removeEventListener('session-timer-update', handleSessionTimerUpdate); document.removeEventListener('session-timer-update', handleSessionTimerUpdate);
document.removeEventListener('force-header-update', handleForceHeaderUpdate); document.removeEventListener('force-header-update', handleForceHeaderUpdate);
document.removeEventListener('peer-disconnect', handlePeerDisconnect); document.removeEventListener('peer-disconnect', handlePeerDisconnect);
document.removeEventListener('new-connection', handleNewConnection); document.removeEventListener('new-connection', handleNewConnection);
document.removeEventListener('connection-cleaned', handleConnectionCleaned);
}; };
}, [sessionManager]); }, [sessionManager]);
if (showExpiredMessage) { if (showExpiredMessage) {
return React.createElement('div', { return React.createElement('div', {
@@ -145,20 +166,33 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
} }
if (!sessionManager) { if (!sessionManager) {
console.log('⏱️ SessionTimer hidden - no sessionManager'); if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - no sessionManager');
setLoggedHidden(true);
}
return null; return null;
} }
if (connectionBroken) { if (connectionBroken) {
console.log('⏱️ SessionTimer hidden - connection broken'); if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - connection broken');
setLoggedHidden(true);
}
return null; return null;
} }
if (!currentTime || currentTime <= 0) { if (!currentTime || currentTime <= 0) {
console.log('⏱️ SessionTimer hidden - no time left'); if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - no time left');
setLoggedHidden(true);
}
return null; return null;
} }
if (loggedHidden) {
setLoggedHidden(false);
}
const totalMinutes = Math.floor(currentTime / (60 * 1000)); const totalMinutes = Math.floor(currentTime / (60 * 1000));
const totalSeconds = Math.floor(currentTime / 1000); const totalSeconds = Math.floor(currentTime / 1000);
@@ -179,8 +213,8 @@ const SessionTimer = ({ timeLeft, sessionType, sessionManager }) => {
}; };
const getTimerStyle = () => { const getTimerStyle = () => {
const totalDuration = sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000; const totalDuration = sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000;
const timeProgress = (totalDuration - currentTime) / totalDuration; const timeProgress = (totalDuration - currentTime) / totalDuration;
let backgroundColor, textColor, iconColor, iconClass, shouldPulse; let backgroundColor, textColor, iconColor, iconClass, shouldPulse;
@@ -247,4 +281,4 @@ window.updateSessionTimer = (newTimeLeft, newSessionType) => {
})); }));
}; };
console.log('✅ SessionTimer loaded with fixes and improvements'); console.log('✅ SessionTimer loaded with anti-spam logging fixes');

View File

@@ -2505,6 +2505,13 @@ async autoEnableSecurityFeatures() {
securityLevel: offerPackage.securityLevel.level securityLevel: offerPackage.securityLevel.level
}); });
document.dispatchEvent(new CustomEvent('new-connection', {
detail: {
type: 'offer',
timestamp: Date.now()
}
}));
return offerPackage; return offerPackage;
} catch (error) { } catch (error) {
window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Enhanced secure offer creation failed', { window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Enhanced secure offer creation failed', {
@@ -2712,6 +2719,13 @@ async autoEnableSecurityFeatures() {
securityLevel: answerPackage.securityLevel.level securityLevel: answerPackage.securityLevel.level
}); });
document.dispatchEvent(new CustomEvent('new-connection', {
detail: {
type: 'answer',
timestamp: Date.now()
}
}));
return answerPackage; return answerPackage;
} catch (error) { } catch (error) {
window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Enhanced secure answer creation failed', { window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Enhanced secure answer creation failed', {
@@ -3252,6 +3266,13 @@ async autoEnableSecurityFeatures() {
setTimeout(() => { setTimeout(() => {
this.sendDisconnectNotification(); this.sendDisconnectNotification();
}, 100); }, 100);
document.dispatchEvent(new CustomEvent('peer-disconnect', {
detail: {
reason: 'user_disconnect',
timestamp: Date.now()
}
}));
setTimeout(() => { setTimeout(() => {
this.cleanupConnection(); this.cleanupConnection();
@@ -3263,6 +3284,13 @@ async autoEnableSecurityFeatures() {
this.isVerified = false; this.isVerified = false;
this.onMessage('🔌 Connection lost. Attempting to reconnect...', 'system'); this.onMessage('🔌 Connection lost. Attempting to reconnect...', 'system');
document.dispatchEvent(new CustomEvent('peer-disconnect', {
detail: {
reason: 'connection_lost',
timestamp: Date.now()
}
}));
setTimeout(() => { setTimeout(() => {
if (!this.intentionalDisconnect) { if (!this.intentionalDisconnect) {
this.attemptReconnection(); this.attemptReconnection();
@@ -3322,6 +3350,13 @@ async autoEnableSecurityFeatures() {
this.onKeyExchange(''); this.onKeyExchange('');
this.onVerificationRequired(''); this.onVerificationRequired('');
document.dispatchEvent(new CustomEvent('peer-disconnect', {
detail: {
reason: reason,
timestamp: Date.now()
}
}));
setTimeout(() => { setTimeout(() => {
this.cleanupConnection(); this.cleanupConnection();
}, 2000); }, 2000);
@@ -3390,6 +3425,13 @@ async autoEnableSecurityFeatures() {
// IMPORTANT: Clearing security logs // IMPORTANT: Clearing security logs
window.EnhancedSecureCryptoUtils.secureLog.clearLogs(); window.EnhancedSecureCryptoUtils.secureLog.clearLogs();
document.dispatchEvent(new CustomEvent('connection-cleaned', {
detail: {
timestamp: Date.now(),
reason: this.intentionalDisconnect ? 'user_cleanup' : 'automatic_cleanup'
}
}));
// Notifying the UI about complete cleanup // Notifying the UI about complete cleanup
this.onStatusChange('disconnected'); this.onStatusChange('disconnected');
this.onKeyExchange(''); this.onKeyExchange('');

View File

@@ -55,9 +55,24 @@ class PayPerSessionManager {
this.startDemoSessionCleanup(); this.startDemoSessionCleanup();
this.startActiveDemoSessionCleanup(); this.startActiveDemoSessionCleanup();
this.globalDemoCounter = 0;
this.memoryStorage = new Map();
this.currentTabId = null;
this.tabHeartbeatInterval = null;
this.initializePersistentStorage();
this.performEnhancedCleanup();
const multiTabCheck = this.checkMultiTabProtection();
if (!multiTabCheck.allowed) {
console.warn('❌ Multi-tab protection triggered:', multiTabCheck.message);
}
console.log('💰 PayPerSessionManager initialized with ENHANCED secure demo mode'); console.log('💰 PayPerSessionManager initialized with ENHANCED secure demo mode');
setInterval(() => {
this.savePersistentData();
}, 30000);
console.log('💰 PayPerSessionManager initialized with ENHANCED secure demo mode and auto-save');
} }
// ============================================ // ============================================
@@ -128,9 +143,10 @@ class PayPerSessionManager {
} }
// IMPROVED user fingerprint generation // IMPROVED user fingerprint generation
generateUserFingerprint() { generateAdvancedUserFingerprint() {
try { try {
const components = [ // Базовые компоненты (как было)
const basicComponents = [
navigator.userAgent || '', navigator.userAgent || '',
navigator.language || '', navigator.language || '',
screen.width + 'x' + screen.height, screen.width + 'x' + screen.height,
@@ -144,136 +160,382 @@ class PayPerSessionManager {
navigator.maxTouchPoints || 0, navigator.maxTouchPoints || 0,
navigator.onLine ? '1' : '0' navigator.onLine ? '1' : '0'
]; ];
// НОВЫЕ КОМПОНЕНТЫ ДЛЯ HARDWARE BINDING
const hardwareComponents = [];
// Create a more secure hash // WebGL отпечаток (очень сложно подделать)
let hash = 0; try {
const str = components.join('|'); const canvas = document.createElement('canvas');
for (let i = 0; i < str.length; i++) { const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
const char = str.charCodeAt(i); if (gl) {
hash = ((hash << 5) - hash) + char; const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
hash = hash & hash; if (debugInfo) {
hardwareComponents.push(gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || '');
hardwareComponents.push(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || '');
}
hardwareComponents.push(gl.getParameter(gl.VERSION) || '');
hardwareComponents.push(gl.getParameter(gl.SHADING_LANGUAGE_VERSION) || '');
}
} catch (e) {
hardwareComponents.push('webgl_error');
}
// Canvas отпечаток (уникален для каждого устройства)
try {
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 50;
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillText('SecureBit Demo Fingerprint 🔒', 2, 2);
ctx.fillStyle = 'rgba(255,0,0,0.5)';
ctx.fillRect(50, 10, 20, 20);
hardwareComponents.push(canvas.toDataURL());
} catch (e) {
hardwareComponents.push('canvas_error');
}
// Аудио отпечаток (очень стабилен)
try {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const analyser = audioContext.createAnalyser();
const gain = audioContext.createGain();
oscillator.connect(analyser);
analyser.connect(gain);
gain.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(1000, audioContext.currentTime);
gain.gain.setValueAtTime(0, audioContext.currentTime);
hardwareComponents.push(audioContext.sampleRate.toString());
hardwareComponents.push(audioContext.state);
hardwareComponents.push(analyser.frequencyBinCount.toString());
audioContext.close();
} catch (e) {
hardwareComponents.push('audio_error');
}
// Производительность CPU (стабильна для устройства)
const cpuBenchmark = this.performCPUBenchmark();
hardwareComponents.push(cpuBenchmark);
// Объединяем все компоненты
const allComponents = [...basicComponents, ...hardwareComponents];
// Создаем несколько уровней хеширования
let primaryHash = 0;
let secondaryHash = 0;
let tertiaryHash = 0;
const primaryStr = allComponents.slice(0, 8).join('|');
const secondaryStr = allComponents.slice(8, 16).join('|');
const tertiaryStr = allComponents.slice(16).join('|');
// Первичный хеш
for (let i = 0; i < primaryStr.length; i++) {
const char = primaryStr.charCodeAt(i);
primaryHash = ((primaryHash << 7) - primaryHash) + char;
primaryHash = primaryHash & primaryHash;
} }
// Add extra salt for stability // Вторичный хеш
const salt = 'securebit_demo_2024'; for (let i = 0; i < secondaryStr.length; i++) {
const saltedStr = str + salt; const char = secondaryStr.charCodeAt(i);
let saltedHash = 0; secondaryHash = ((secondaryHash << 11) - secondaryHash) + char;
for (let i = 0; i < saltedStr.length; i++) { secondaryHash = secondaryHash & secondaryHash;
const char = saltedStr.charCodeAt(i);
saltedHash = ((saltedHash << 5) - saltedHash) + char;
saltedHash = saltedHash & saltedHash;
} }
return Math.abs(hash).toString(36) + '_' + Math.abs(saltedHash).toString(36); // Третичный хеш
for (let i = 0; i < tertiaryStr.length; i++) {
const char = tertiaryStr.charCodeAt(i);
tertiaryHash = ((tertiaryHash << 13) - tertiaryHash) + char;
tertiaryHash = tertiaryHash & tertiaryHash;
}
// Комбинированный отпечаток
const combined = `${Math.abs(primaryHash).toString(36)}_${Math.abs(secondaryHash).toString(36)}_${Math.abs(tertiaryHash).toString(36)}`;
console.log('🔒 Enhanced fingerprint generated:', {
components: allComponents.length,
primaryLength: primaryStr.length,
secondaryLength: secondaryStr.length,
tertiaryLength: tertiaryStr.length,
fingerprintLength: combined.length
});
return combined;
} catch (error) { } catch (error) {
console.warn('Failed to generate user fingerprint:', error); console.warn('Failed to generate enhanced fingerprint:', error);
return 'fallback_' + Math.random().toString(36).substr(2, 9); // Fallback к более простому отпечатку
return 'fallback_' + Date.now().toString(36) + '_' + Math.random().toString(36).substr(2, 9);
} }
} }
// COMPLETELY REWRITTEN demo session limits check performCPUBenchmark() {
checkDemoSessionLimits(userFingerprint) { const start = performance.now();
const userData = this.demoSessions.get(userFingerprint); let result = 0;
const now = Date.now();
console.log(`🔍 Checking demo limits for user ${userFingerprint.substring(0, 12)}...`); for (let i = 0; i < 100000; i++) {
result += Math.sin(i) * Math.cos(i);
}
// CHECK 1: Global limit of simultaneous demo sessions const end = performance.now();
if (this.activeDemoSessions.size >= this.maxGlobalDemoSessions) { const duration = Math.round(end - start);
console.log(`❌ Global demo limit reached: ${this.activeDemoSessions.size}/${this.maxGlobalDemoSessions}`);
if (duration < 5) return 'fast_cpu';
if (duration < 15) return 'medium_cpu';
if (duration < 30) return 'slow_cpu';
return 'very_slow_cpu';
}
initializePersistentStorage() {
this.storageKeys = {
demoSessions: 'sb_demo_sessions_v2',
completedSessions: 'sb_completed_sessions_v2',
globalCounter: 'sb_global_demo_counter_v2',
lastCleanup: 'sb_last_cleanup_v2',
hardwareFingerprint: 'sb_hw_fingerprint_v2'
};
this.loadPersistentData();
}
loadPersistentData() {
try {
const savedDemoSessions = this.getFromStorage(this.storageKeys.demoSessions);
if (savedDemoSessions) {
const parsed = JSON.parse(savedDemoSessions);
for (const [key, value] of Object.entries(parsed)) {
this.demoSessions.set(key, value);
}
}
const savedCompletedSessions = this.getFromStorage(this.storageKeys.completedSessions);
if (savedCompletedSessions) {
const parsed = JSON.parse(savedCompletedSessions);
for (const [key, value] of Object.entries(parsed)) {
this.completedDemoSessions.set(key, value);
}
}
const savedGlobalCounter = this.getFromStorage(this.storageKeys.globalCounter);
if (savedGlobalCounter) {
this.globalDemoCounter = parseInt(savedGlobalCounter) || 0;
} else {
this.globalDemoCounter = 0;
}
console.log('📊 Persistent data loaded:', {
demoSessions: this.demoSessions.size,
completedSessions: this.completedDemoSessions.size,
globalCounter: this.globalDemoCounter
});
} catch (error) {
console.warn('Failed to load persistent data:', error);
this.globalDemoCounter = 0;
}
}
savePersistentData() {
try {
const demoSessionsObj = Object.fromEntries(this.demoSessions);
this.setToStorage(this.storageKeys.demoSessions, JSON.stringify(demoSessionsObj));
const completedSessionsObj = Object.fromEntries(this.completedDemoSessions);
this.setToStorage(this.storageKeys.completedSessions, JSON.stringify(completedSessionsObj));
this.setToStorage(this.storageKeys.globalCounter, this.globalDemoCounter.toString());
this.setToStorage(this.storageKeys.lastCleanup, Date.now().toString());
} catch (error) {
console.warn('Failed to save persistent data:', error);
}
}
getFromStorage(key) {
try {
if (typeof localStorage !== 'undefined') {
const value = localStorage.getItem(key);
if (value) return value;
}
} catch (e) {}
try {
if (typeof sessionStorage !== 'undefined') {
const value = sessionStorage.getItem(key);
if (value) return value;
}
} catch (e) {}
try {
if ('caches' in window) {
}
} catch (e) {}
return null;
}
setToStorage(key, value) {
try {
if (typeof localStorage !== 'undefined') {
localStorage.setItem(key, value);
}
} catch (e) {}
try {
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem(key, value);
}
} catch (e) {}
if (!this.memoryStorage) this.memoryStorage = new Map();
this.memoryStorage.set(key, value);
}
checkAntiResetProtection(userFingerprint) {
if (!this.globalDemoCounter) {
this.globalDemoCounter = 0;
}
const hardwareFingerprint = this.getHardwareFingerprint();
const savedHardwareFingerprint = this.getFromStorage(this.storageKeys.hardwareFingerprint);
if (savedHardwareFingerprint && savedHardwareFingerprint !== hardwareFingerprint) {
console.warn('🚨 Hardware fingerprint mismatch detected - possible reset attempt');
this.globalDemoCounter += 5;
this.savePersistentData();
return { return {
allowed: false, isValid: false,
reason: 'hardware_mismatch',
penalty: 5
};
}
this.setToStorage(this.storageKeys.hardwareFingerprint, hardwareFingerprint);
if (this.globalDemoCounter >= 10) {
return {
isValid: false,
reason: 'global_limit_exceeded', reason: 'global_limit_exceeded',
message: `Too many demo sessions active globally (${this.activeDemoSessions.size}/${this.maxGlobalDemoSessions}). Please try again later.`, globalCount: this.globalDemoCounter
remaining: 0,
debugInfo: `Global sessions: ${this.activeDemoSessions.size}/${this.maxGlobalDemoSessions}`
}; };
} }
if (!userData) { return {
// First demo session for this user isValid: true,
console.log(`✅ First demo session for user ${userFingerprint.substring(0, 12)}`); globalCount: this.globalDemoCounter
return { };
allowed: true, }
reason: 'first_demo_session',
remaining: this.maxDemoSessionsPerUser, getHardwareFingerprint() {
debugInfo: 'First time user' const components = [];
};
// CPU информация
components.push(navigator.hardwareConcurrency || 0);
components.push(navigator.deviceMemory || 0);
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
if (gl) {
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
components.push(gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || '');
components.push(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || '');
}
}
} catch (e) {
components.push('webgl_unavailable');
} }
// CHECK 2: Limit sessions per 24 hours (STRICT check) components.push(screen.width);
const sessionsLast24h = userData.sessions.filter(session => components.push(screen.height);
now - session.timestamp < this.demoCooldownPeriod components.push(screen.colorDepth);
);
components.push(Intl.DateTimeFormat().resolvedOptions().timeZone);
console.log(`📊 Sessions in last 24h for user ${userFingerprint.substring(0, 12)}: ${sessionsLast24h.length}/${this.maxDemoSessionsPerUser}`); let hash = 0;
const str = components.join('|');
if (sessionsLast24h.length >= this.maxDemoSessionsPerUser) { for (let i = 0; i < str.length; i++) {
const oldestSession = Math.min(...sessionsLast24h.map(s => s.timestamp)); const char = str.charCodeAt(i);
const timeUntilNext = this.demoCooldownPeriod - (now - oldestSession); hash = ((hash << 5) - hash) + char;
hash = hash & hash;
console.log(`❌ Daily demo limit exceeded for user ${userFingerprint.substring(0, 12)}`);
return {
allowed: false,
reason: 'daily_limit_exceeded',
timeUntilNext: timeUntilNext,
message: `Daily demo limit reached (${this.maxDemoSessionsPerUser}/day). Next session available in ${Math.ceil(timeUntilNext / (60 * 1000))} minutes.`,
remaining: 0,
debugInfo: `Used ${sessionsLast24h.length}/${this.maxDemoSessionsPerUser} today`
};
} }
// CHECK 3: Cooldown between sessions (FIXED LOGIC) return Math.abs(hash).toString(36);
if (userData.lastUsed && (now - userData.lastUsed) < this.demoSessionCooldown) { }
const timeUntilNext = this.demoSessionCooldown - (now - userData.lastUsed);
const minutesLeft = Math.ceil(timeUntilNext / (60 * 1000)); registerEnhancedDemoSessionUsage(userFingerprint, preimage) {
// Вызываем оригинальный метод
console.log(`⏰ Cooldown active for user ${userFingerprint.substring(0, 12)}: ${minutesLeft} minutes`); const session = this.registerDemoSessionUsage(userFingerprint, preimage);
return {
allowed: false,
reason: 'session_cooldown',
timeUntilNext: timeUntilNext,
message: `Please wait ${minutesLeft} minutes between demo sessions. This prevents abuse and ensures fair access for all users.`,
remaining: this.maxDemoSessionsPerUser - sessionsLast24h.length,
debugInfo: `Cooldown: ${minutesLeft}min left, last used: ${Math.round((now - userData.lastUsed) / (60 * 1000))}min ago`
};
}
// CHECK 4: NEW - Check for completed sessions // Дополнительно сохраняем в persistent storage
const completedSessions = this.completedDemoSessions.get(userFingerprint) || []; this.savePersistentData();
const recentCompletedSessions = completedSessions.filter(session =>
now - session.endTime < this.minTimeBetweenCompletedSessions console.log('📊 Enhanced demo session registered:', {
); userFingerprint: userFingerprint.substring(0, 12),
globalCount: this.globalDemoCounter,
sessionId: session.sessionId,
timestamp: new Date().toISOString()
});
if (recentCompletedSessions.length > 0) { return session;
const lastCompletedSession = Math.max(...recentCompletedSessions.map(s => s.endTime)); }
const timeUntilNext = this.minTimeBetweenCompletedSessions - (now - lastCompletedSession);
// COMPLETELY REWRITTEN demo session limits check
console.log(`⏰ Recent session completed, waiting period active for user ${userFingerprint.substring(0, 12)}`); checkEnhancedDemoSessionLimits(userFingerprint) {
const antiResetCheck = this.checkAntiResetProtection(userFingerprint);
if (!antiResetCheck.isValid) {
return { return {
allowed: false, allowed: false,
reason: 'recent_session_completed', reason: antiResetCheck.reason,
timeUntilNext: timeUntilNext, message: this.getAntiResetMessage(antiResetCheck),
message: `Please wait ${Math.ceil(timeUntilNext / (60 * 1000))} minutes after your last session before starting a new one.`, globalCount: antiResetCheck.globalCount,
remaining: this.maxDemoSessionsPerUser - sessionsLast24h.length, penalty: antiResetCheck.penalty
debugInfo: `Last session ended ${Math.round((now - lastCompletedSession) / (60 * 1000))}min ago`
}; };
} }
console.log(`Demo session approved for user ${userFingerprint.substring(0, 12)}`); const regularCheck = this.checkDemoSessionLimits(userFingerprint);
return {
allowed: true, if (regularCheck.allowed) {
reason: 'within_limits', this.globalDemoCounter++;
remaining: this.maxDemoSessionsPerUser - sessionsLast24h.length, this.savePersistentData();
debugInfo: `Available: ${this.maxDemoSessionsPerUser - sessionsLast24h.length}/${this.maxDemoSessionsPerUser}` }
return {
...regularCheck,
globalCount: this.globalDemoCounter
}; };
} }
getAntiResetMessage(antiResetCheck) {
switch (antiResetCheck.reason) {
case 'hardware_mismatch':
return 'Обнаружена попытка сброса ограничений. Доступ к демо-режиму временно ограничен.';
case 'global_limit_exceeded':
return `Глобальный лимит демо-сессий превышен (${antiResetCheck.globalCount}/10). Для продолжения требуется оплаченная сессия.`;
default:
return 'Доступ к демо-режиму ограничен по соображениям безопасности.';
}
}
// FIXED demo session usage registration // FIXED demo session usage registration
registerDemoSessionUsage(userFingerprint, preimage) { registerDemoSessionUsage(userFingerprint, preimage) {
const now = Date.now(); const now = Date.now();
const userData = this.demoSessions.get(userFingerprint) || { const userData = this.demoSessions.get(userFingerprint) || {
count: 0, count: 0,
@@ -312,6 +574,131 @@ class PayPerSessionManager {
return newSession; return newSession;
} }
performEnhancedCleanup() {
const now = Date.now();
const lastCleanup = parseInt(this.getFromStorage(this.storageKeys.lastCleanup)) || 0;
if (now - lastCleanup < 6 * 60 * 60 * 1000) {
return;
}
console.log('🧹 Performing enhanced cleanup...');
const maxAge = 25 * 60 * 60 * 1000;
let cleanedSessions = 0;
for (const [identifier, data] of this.demoSessions.entries()) {
if (now - data.lastUsed > maxAge) {
this.demoSessions.delete(identifier);
cleanedSessions++;
}
}
let cleanedCompleted = 0;
for (const [identifier, sessions] of this.completedDemoSessions.entries()) {
const filteredSessions = sessions.filter(session =>
now - session.endTime < maxAge
);
if (filteredSessions.length === 0) {
this.completedDemoSessions.delete(identifier);
cleanedCompleted++;
} else {
this.completedDemoSessions.set(identifier, filteredSessions);
}
}
const weekAgo = 7 * 24 * 60 * 60 * 1000;
if (now - lastCleanup > weekAgo) {
this.globalDemoCounter = Math.max(0, this.globalDemoCounter - 3);
console.log('🔄 Global demo counter reset (weekly):', this.globalDemoCounter);
}
this.savePersistentData();
console.log('✅ Enhanced cleanup completed:', {
cleanedSessions,
cleanedCompleted,
globalCounter: this.globalDemoCounter
});
}
checkMultiTabProtection() {
const tabId = 'tab_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const activeTabsKey = 'sb_active_tabs';
try {
const activeTabsStr = this.getFromStorage(activeTabsKey);
const activeTabs = activeTabsStr ? JSON.parse(activeTabsStr) : [];
const now = Date.now();
const validTabs = activeTabs.filter(tab => now - tab.timestamp < 30000);
if (validTabs.length >= 2) {
return {
allowed: false,
reason: 'multiple_tabs',
message: 'Демо-режим доступен только в одной вкладке одновременно.'
};
}
validTabs.push({
tabId: tabId,
timestamp: now
});
this.setToStorage(activeTabsKey, JSON.stringify(validTabs));
this.currentTabId = tabId;
this.startTabHeartbeat();
return {
allowed: true,
tabId: tabId
};
} catch (error) {
console.warn('Multi-tab protection error:', error);
return { allowed: true };
}
}
startTabHeartbeat() {
if (this.tabHeartbeatInterval) {
clearInterval(this.tabHeartbeatInterval);
}
this.tabHeartbeatInterval = setInterval(() => {
this.updateTabHeartbeat();
}, 10000);
}
updateTabHeartbeat() {
if (!this.currentTabId) return;
try {
const activeTabsKey = 'sb_active_tabs';
const activeTabsStr = this.getFromStorage(activeTabsKey);
const activeTabs = activeTabsStr ? JSON.parse(activeTabsStr) : [];
// Обновляем timestamp для текущей вкладки
const updatedTabs = activeTabs.map(tab => {
if (tab.tabId === this.currentTabId) {
return {
...tab,
timestamp: Date.now()
};
}
return tab;
});
this.setToStorage(activeTabsKey, JSON.stringify(updatedTabs));
} catch (error) {
console.warn('Tab heartbeat update failed:', error);
}
}
// NEW method: Register demo session completion // NEW method: Register demo session completion
registerDemoSessionCompletion(userFingerprint, sessionDuration, preimage) { registerDemoSessionCompletion(userFingerprint, sessionDuration, preimage) {
const now = Date.now(); const now = Date.now();
@@ -504,9 +891,9 @@ class PayPerSessionManager {
throw new Error('Demo preimage timestamp from future - possible clock manipulation'); throw new Error('Demo preimage timestamp from future - possible clock manipulation');
} }
// CHECK 4: Custom Limits // CHECK 4: ИСПРАВЛЕННЫЙ вызов лимитов - используем ПРАВИЛЬНЫЙ метод
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint(); // ИСПРАВЛЕНО!
const limitsCheck = this.checkDemoSessionLimits(userFingerprint); const limitsCheck = this.checkEnhancedDemoSessionLimits(userFingerprint); // ИСПРАВЛЕНО!
if (!limitsCheck.allowed) { if (!limitsCheck.allowed) {
throw new Error(`Demo session limits exceeded: ${limitsCheck.message}`); throw new Error(`Demo session limits exceeded: ${limitsCheck.message}`);
@@ -514,7 +901,7 @@ class PayPerSessionManager {
// FIX: For demo sessions, do NOT add preimage to usedPreimages here, // FIX: For demo sessions, do NOT add preimage to usedPreimages here,
// as this will only be done after successful activation // as this will only be done after successful activation
this.registerDemoSessionUsage(userFingerprint, preimage); this.registerEnhancedDemoSessionUsage(userFingerprint, preimage); // ИСПРАВЛЕНО!
console.log('✅ Demo preimage ENHANCED validation passed'); console.log('✅ Demo preimage ENHANCED validation passed');
return true; return true;
@@ -552,6 +939,7 @@ class PayPerSessionManager {
} }
} }
// ============================================ // ============================================
// LIGHTNING NETWORK INTEGRATION // LIGHTNING NETWORK INTEGRATION
// ============================================ // ============================================
@@ -933,9 +1321,9 @@ class PayPerSessionManager {
}; };
} }
// ADDITIONAL check at activation level // ИСПРАВЛЕННЫЙ вызов - используем правильные методы
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint();
const demoCheck = this.checkDemoSessionLimits(userFingerprint); const demoCheck = this.checkEnhancedDemoSessionLimits(userFingerprint);
if (!demoCheck.allowed) { if (!demoCheck.allowed) {
console.log(`⚠️ Demo session cooldown active, but allowing activation for development`); console.log(`⚠️ Demo session cooldown active, but allowing activation for development`);
@@ -1115,7 +1503,7 @@ class PayPerSessionManager {
handleDemoSessionExpiry(preimage) { handleDemoSessionExpiry(preimage) {
if (this.currentSession && this.currentSession.preimage === preimage) { if (this.currentSession && this.currentSession.preimage === preimage) {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint(); // ИСПРАВЛЕНО!
const sessionDuration = Date.now() - this.currentSession.startTime; const sessionDuration = Date.now() - this.currentSession.startTime;
this.registerDemoSessionCompletion(userFingerprint, sessionDuration, preimage); this.registerDemoSessionCompletion(userFingerprint, sessionDuration, preimage);
@@ -1145,7 +1533,7 @@ class PayPerSessionManager {
const expiredSession = this.currentSession; const expiredSession = this.currentSession;
if (expiredSession && expiredSession.isDemo) { if (expiredSession && expiredSession.isDemo) {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint(); // ИСПРАВЛЕНО!
const sessionDuration = Date.now() - expiredSession.startTime; const sessionDuration = Date.now() - expiredSession.startTime;
this.registerDemoSessionCompletion(userFingerprint, sessionDuration, expiredSession.preimage); this.registerDemoSessionCompletion(userFingerprint, sessionDuration, expiredSession.preimage);
} }
@@ -1194,8 +1582,8 @@ class PayPerSessionManager {
// UPDATED demo session creation // UPDATED demo session creation
createDemoSession() { createDemoSession() {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint(); // ИСПРАВЛЕНО!
const demoCheck = this.checkDemoSessionLimits(userFingerprint); const demoCheck = this.checkEnhancedDemoSessionLimits(userFingerprint); // ИСПРАВЛЕНО!
if (!demoCheck.allowed) { if (!demoCheck.allowed) {
return { return {
@@ -1248,7 +1636,7 @@ class PayPerSessionManager {
// UPDATED information about demo limits // UPDATED information about demo limits
getDemoSessionInfo() { getDemoSessionInfo() {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint();
const userData = this.demoSessions.get(userFingerprint); const userData = this.demoSessions.get(userFingerprint);
const now = Date.now(); const now = Date.now();
@@ -1519,7 +1907,7 @@ class PayPerSessionManager {
// IMPORTANT: For demo sessions, we register forced termination // IMPORTANT: For demo sessions, we register forced termination
if (resetSession && resetSession.isDemo) { if (resetSession && resetSession.isDemo) {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint();
const sessionDuration = Date.now() - resetSession.startTime; const sessionDuration = Date.now() - resetSession.startTime;
this.registerDemoSessionCompletion(userFingerprint, sessionDuration, resetSession.preimage); this.registerDemoSessionCompletion(userFingerprint, sessionDuration, resetSession.preimage);
} }
@@ -1555,7 +1943,7 @@ class PayPerSessionManager {
// IMPORTANT: We register the end of the current demo session during cleanup // IMPORTANT: We register the end of the current demo session during cleanup
if (this.currentSession && this.currentSession.isDemo) { if (this.currentSession && this.currentSession.isDemo) {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint(); // ИСПРАВЛЕНО!
const sessionDuration = Date.now() - this.currentSession.startTime; const sessionDuration = Date.now() - this.currentSession.startTime;
this.registerDemoSessionCompletion(userFingerprint, sessionDuration, this.currentSession.preimage); this.registerDemoSessionCompletion(userFingerprint, sessionDuration, this.currentSession.preimage);
} }
@@ -1588,7 +1976,7 @@ class PayPerSessionManager {
} }
getVerifiedDemoSession() { getVerifiedDemoSession() {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint();
const userData = this.demoSessions.get(userFingerprint); const userData = this.demoSessions.get(userFingerprint);
console.log('🔍 Searching for verified demo session:', { console.log('🔍 Searching for verified demo session:', {
@@ -1644,8 +2032,106 @@ class PayPerSessionManager {
return verifiedSession; return verifiedSession;
} }
checkDemoSessionLimits(userFingerprint) {
const userData = this.demoSessions.get(userFingerprint);
const now = Date.now();
console.log(`🔍 Checking demo limits for user ${userFingerprint.substring(0, 12)}...`);
// CHECK 1: Global limit of simultaneous demo sessions
if (this.activeDemoSessions.size >= this.maxGlobalDemoSessions) {
console.log(`❌ Global demo limit reached: ${this.activeDemoSessions.size}/${this.maxGlobalDemoSessions}`);
return {
allowed: false,
reason: 'global_limit_exceeded',
message: `Too many demo sessions active globally (${this.activeDemoSessions.size}/${this.maxGlobalDemoSessions}). Please try again later.`,
remaining: 0,
debugInfo: `Global sessions: ${this.activeDemoSessions.size}/${this.maxGlobalDemoSessions}`
};
}
if (!userData) {
// First demo session for this user
console.log(`✅ First demo session for user ${userFingerprint.substring(0, 12)}`);
return {
allowed: true,
reason: 'first_demo_session',
remaining: this.maxDemoSessionsPerUser,
debugInfo: 'First time user'
};
}
// CHECK 2: Limit sessions per 24 hours (STRICT check)
const sessionsLast24h = userData.sessions.filter(session =>
now - session.timestamp < this.demoCooldownPeriod
);
console.log(`📊 Sessions in last 24h for user ${userFingerprint.substring(0, 12)}: ${sessionsLast24h.length}/${this.maxDemoSessionsPerUser}`);
if (sessionsLast24h.length >= this.maxDemoSessionsPerUser) {
const oldestSession = Math.min(...sessionsLast24h.map(s => s.timestamp));
const timeUntilNext = this.demoCooldownPeriod - (now - oldestSession);
console.log(`❌ Daily demo limit exceeded for user ${userFingerprint.substring(0, 12)}`);
return {
allowed: false,
reason: 'daily_limit_exceeded',
timeUntilNext: timeUntilNext,
message: `Daily demo limit reached (${this.maxDemoSessionsPerUser}/day). Next session available in ${Math.ceil(timeUntilNext / (60 * 1000))} minutes.`,
remaining: 0,
debugInfo: `Used ${sessionsLast24h.length}/${this.maxDemoSessionsPerUser} today`
};
}
// CHECK 3: Cooldown between sessions (FIXED LOGIC)
if (userData.lastUsed && (now - userData.lastUsed) < this.demoSessionCooldown) {
const timeUntilNext = this.demoSessionCooldown - (now - userData.lastUsed);
const minutesLeft = Math.ceil(timeUntilNext / (60 * 1000));
console.log(`⏰ Cooldown active for user ${userFingerprint.substring(0, 12)}: ${minutesLeft} minutes`);
return {
allowed: false,
reason: 'session_cooldown',
timeUntilNext: timeUntilNext,
message: `Please wait ${minutesLeft} minutes between demo sessions. This prevents abuse and ensures fair access for all users.`,
remaining: this.maxDemoSessionsPerUser - sessionsLast24h.length,
debugInfo: `Cooldown: ${minutesLeft}min left, last used: ${Math.round((now - userData.lastUsed) / (60 * 1000))}min ago`
};
}
// CHECK 4: NEW - Check for completed sessions
const completedSessions = this.completedDemoSessions.get(userFingerprint) || [];
const recentCompletedSessions = completedSessions.filter(session =>
now - session.endTime < this.minTimeBetweenCompletedSessions
);
if (recentCompletedSessions.length > 0) {
const lastCompletedSession = Math.max(...recentCompletedSessions.map(s => s.endTime));
const timeUntilNext = this.minTimeBetweenCompletedSessions - (now - lastCompletedSession);
console.log(`⏰ Recent session completed, waiting period active for user ${userFingerprint.substring(0, 12)}`);
return {
allowed: false,
reason: 'recent_session_completed',
timeUntilNext: timeUntilNext,
message: `Please wait ${Math.ceil(timeUntilNext / (60 * 1000))} minutes after your last session before starting a new one.`,
remaining: this.maxDemoSessionsPerUser - sessionsLast24h.length,
debugInfo: `Last session ended ${Math.round((now - lastCompletedSession) / (60 * 1000))}min ago`
};
}
console.log(`✅ Demo session approved for user ${userFingerprint.substring(0, 12)}`);
return {
allowed: true,
reason: 'within_limits',
remaining: this.maxDemoSessionsPerUser - sessionsLast24h.length,
debugInfo: `Available: ${this.maxDemoSessionsPerUser - sessionsLast24h.length}/${this.maxDemoSessionsPerUser}`
};
}
createDemoSessionForActivation() { createDemoSessionForActivation() {
const userFingerprint = this.generateUserFingerprint(); const userFingerprint = this.generateAdvancedUserFingerprint(); // ИСПРАВЛЕНО!
if (this.activeDemoSessions.size >= this.maxGlobalDemoSessions) { if (this.activeDemoSessions.size >= this.maxGlobalDemoSessions) {
return { return {