From 7ee5ec6208a6414f03c162b7ba4650747c0f401a Mon Sep 17 00:00:00 2001 From: lockbitchat Date: Wed, 20 Aug 2025 23:34:56 -0400 Subject: [PATCH] Security: Implement secure logging to prevent data leaks in console - Add production mode detection and secure logging system - Replace console.log with _secureLog() that sanitizes sensitive data - Hide encryption keys, message content, and security details from logs - Implement log level control (production: warn+error only, dev: debug+) - Add data sanitization for keys, tokens, buffers, and sensitive strings - Prevent log spam with automatic rate limiting and cleanup - Maintain useful debugging info while protecting user privacy - Add automatic memory cleanup for log counters Security Impact: HIGH - Prevents sensitive data exposure through browser console Changes: - Production: Only critical errors/warnings logged - Development: Safe debugging info without sensitive content - All message content, encryption keys, and tokens are now sanitized - Automatic detection of production vs development environment --- src/network/EnhancedSecureWebRTCManager.js | 304 +++++++++++++++++---- 1 file changed, 248 insertions(+), 56 deletions(-) diff --git a/src/network/EnhancedSecureWebRTCManager.js b/src/network/EnhancedSecureWebRTCManager.js index b904994..3a1d0a1 100644 --- a/src/network/EnhancedSecureWebRTCManager.js +++ b/src/network/EnhancedSecureWebRTCManager.js @@ -89,6 +89,12 @@ class EnhancedSecureWebRTCManager { SYSTEM_MESSAGE: 'SYSTEM_MESSAGE_FILTERED' }; constructor(onMessage, onStatusChange, onKeyExchange, onVerificationRequired, onAnswerError = null) { + // Определяем режим работы + this._isProductionMode = this._detectProductionMode(); + this._debugMode = !this._isProductionMode && window.DEBUG_MODE; + + // Инициализируем защищенную систему логирования + this._initializeSecureLogging(); // Check the availability of the global object this._setupSecureGlobalAPI(); if (!window.EnhancedSecureCryptoUtils) { @@ -103,7 +109,7 @@ class EnhancedSecureWebRTCManager { // НЕ возвращаем детали проверок или чувствительные данные } : null; }; - console.log('🔒 Enhanced WebRTC Manager initialized with secure API'); + this._secureLog('info', '🔒 Enhanced WebRTC Manager initialized with secure API'); this.currentSessionType = null; this.currentSecurityLevel = 'basic'; this.sessionConstraints = null; @@ -281,6 +287,159 @@ class EnhancedSecureWebRTCManager { // ============================================ // HELPER МЕТОДЫ // ============================================ + /** + * Инициализирует защищенную систему логирования + */ + _initializeSecureLogging() { + // Уровни логирования + this._logLevels = { + error: 0, + warn: 1, + info: 2, + debug: 3, + trace: 4 + }; + + // Текущий уровень логирования + this._currentLogLevel = this._isProductionMode ? + this._logLevels.warn : // В production только warnings и errors + this._logLevels.debug; // В development больше информации + + // Счетчик логов для предотвращения спама + this._logCounts = new Map(); + this._maxLogCount = 100; // Максимум логов одного типа + + this._secureLog('info', `🔧 Secure logging initialized (Production: ${this._isProductionMode})`); + } + /** + * Защищенное логирование + * @param {string} level - Уровень лога (error, warn, info, debug, trace) + * @param {string} message - Сообщение + * @param {object} data - Дополнительные данные (будут sanitized) + */ + _secureLog(level, message, data = null) { + // Проверяем уровень логирования + if (this._logLevels[level] > this._currentLogLevel) { + return; // Пропускаем логи ниже текущего уровня + } + + // Предотвращаем спам логов + const logKey = `${level}:${message}`; + const currentCount = this._logCounts.get(logKey) || 0; + + if (currentCount >= this._maxLogCount) { + return; // Слишком много одинаковых логов + } + + this._logCounts.set(logKey, currentCount + 1); + + // Sanitize данные для безопасного логирования + const sanitizedData = data ? this._sanitizeLogData(data) : null; + + // Выводим лог в соответствующий метод консоли + const logMethod = console[level] || console.log; + + if (sanitizedData) { + logMethod(message, sanitizedData); + } else { + logMethod(message); + } + } + /** + * Sanitize данных для логирования + */ + _sanitizeLogData(data) { + if (!data || typeof data !== 'object') { + return data; + } + + // Список опасных ключей, которые нужно скрывать + const sensitiveKeys = [ + 'encryptionKey', 'macKey', 'metadataKey', 'privateKey', 'publicKey', + 'verificationCode', 'sessionSalt', 'sessionId', 'keyFingerprint', + 'password', 'token', 'secret', 'credential', 'auth', 'signature', + 'data', 'message', 'content', 'buffer', 'chunk', 'payload' + ]; + + const sanitized = {}; + + for (const [key, value] of Object.entries(data)) { + const lowerKey = key.toLowerCase(); + + // Проверяем на чувствительные ключи + if (sensitiveKeys.some(sensitiveKey => lowerKey.includes(sensitiveKey))) { + if (typeof value === 'string') { + // Показываем только первые и последние символы + sanitized[key] = value.length > 8 ? + `${value.substring(0, 4)}...${value.substring(value.length - 4)}` : + '[HIDDEN]'; + } else if (value instanceof ArrayBuffer || value instanceof Uint8Array) { + sanitized[key] = `[${value.constructor.name}(${value.byteLength || value.length} bytes)]`; + } else if (value && typeof value === 'object') { + sanitized[key] = '[OBJECT_HIDDEN]'; + } else { + sanitized[key] = '[HIDDEN]'; + } + } else if (key === 'timestamp' || key === 'length' || key === 'size' || key === 'count') { + // Безопасные числовые значения + sanitized[key] = value; + } else if (typeof value === 'boolean') { + // Булевы значения безопасны + sanitized[key] = value; + } else if (typeof value === 'string' && value.length < 100) { + // Короткие строки (если не содержат чувствительную информацию) + if (!this._containsSensitiveContent(value)) { + sanitized[key] = value; + } else { + sanitized[key] = '[SANITIZED]'; + } + } else if (typeof value === 'object' && value !== null) { + // Рекурсивно sanitize вложенных объектов (с ограничением глубины) + sanitized[key] = this._sanitizeLogData(value); + } else { + sanitized[key] = typeof value; + } + } + + return sanitized; + } + /** + * Проверяет содержит ли строка чувствительный контент + */ + _containsSensitiveContent(str) { + if (typeof str !== 'string') return false; + + const sensitivePatterns = [ + /[a-f0-9]{32,}/i, // Hex строки (ключи) + /[A-Za-z0-9+/=]{20,}/, // Base64 строки + /\b[A-Za-z0-9]{20,}\b/, // Длинные алфанумерные строки + /BEGIN\s+(PRIVATE|PUBLIC)\s+KEY/i, // PEM ключи + ]; + + return sensitivePatterns.some(pattern => pattern.test(str)); + } + // ============================================ + // СИСТЕМА ЗАЩИЩЕННОГО ЛОГИРОВАНИЯ + // ============================================ + + /** + * Определяет production mode + */ + _detectProductionMode() { + // Проверяем различные индикаторы production mode + return ( + // Стандартные переменные окружения + (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') || + // Отсутствие debug флагов + (!window.DEBUG_MODE && !window.DEVELOPMENT_MODE) || + // Production домены + (window.location.hostname && !window.location.hostname.includes('localhost') && + !window.location.hostname.includes('127.0.0.1') && + !window.location.hostname.includes('.local')) || + // Минификация кода (примерная проверка) + (typeof window.webpackHotUpdate === 'undefined' && !window.location.search.includes('debug')) + ); + } // ============================================ // ИСПРАВЛЕННЫЙ БЕЗОПАСНЫЙ ГЛОБАЛЬНЫЙ API // ============================================ @@ -648,6 +807,10 @@ class EnhancedSecureWebRTCManager { // Начинаем мониторинг this._startSecurityMonitoring(); + // Запускаем периодическую очистку логов + setInterval(() => { + this._cleanupLogs(); + }, 300000); console.log('✅ Secure WebRTC Manager initialization completed'); } @@ -876,6 +1039,39 @@ class EnhancedSecureWebRTCManager { const filteredResults = Object.values(EnhancedSecureWebRTCManager.FILTERED_RESULTS); return filteredResults.includes(result); } + /** + * Очистка логов для предотвращения утечек памяти + */ + _cleanupLogs() { + // Очищаем счетчики логов если их слишком много + if (this._logCounts.size > 1000) { + this._logCounts.clear(); + this._secureLog('debug', '🧹 Log counts cleared'); + } + } + /** + * Получение статистики логирования (для диагностики) + */ + _getLoggingStats() { + return { + isProductionMode: this._isProductionMode, + debugMode: this._debugMode, + currentLogLevel: this._currentLogLevel, + logCountsSize: this._logCounts.size, + maxLogCount: this._maxLogCount + }; + } + /** + * Экстренное отключение логирования + */ + _emergencyDisableLogging() { + this._currentLogLevel = -1; // Отключаем все логи + this._logCounts.clear(); + this._secureLog = () => {}; // Пустая функция + + // Только критическая ошибка в консоль + console.error('🚨 Logging disabled due to security concerns'); + } initializeFileTransfer() { try { @@ -1496,9 +1692,10 @@ class EnhancedSecureWebRTCManager { } try { - if (window.DEBUG_MODE) { - console.log(`🎭 Sending fake message: ${fakeMessage.pattern} (${fakeMessage.size} bytes)`); - } + this._secureLog('debug', '🎭 Sending fake message', { + pattern: fakeMessage.pattern, + size: fakeMessage.size + }); const fakeData = JSON.stringify({ ...fakeMessage, @@ -1508,18 +1705,16 @@ class EnhancedSecureWebRTCManager { }); const fakeBuffer = new TextEncoder().encode(fakeData); - const encryptedFake = await this.applySecurityLayers(fakeBuffer, true); - this.dataChannel.send(encryptedFake); - if (window.DEBUG_MODE) { - console.log(`🎭 Fake message sent successfully: ${fakeMessage.pattern}`); - } + this._secureLog('debug', '🎭 Fake message sent successfully', { + pattern: fakeMessage.pattern + }); } catch (error) { - if (window.DEBUG_MODE) { - console.error('❌ Failed to send fake message:', error); - } + this._secureLog('error', '❌ Failed to send fake message', { + error: error.message + }); } } @@ -2489,7 +2684,7 @@ async processOrderedPackets() { } try { - console.log('📤 sendMessage called:', { + this._secureLog('debug', '📤 sendMessage called', { hasDataChannel: !!this.dataChannel, dataChannelState: this.dataChannel?.readyState, isInitiator: this.isInitiator, @@ -2497,13 +2692,11 @@ async processOrderedPackets() { connectionState: this.peerConnection?.connectionState }); - console.log('🔍 sendMessage DEBUG:', { + this._secureLog('debug', '🔍 sendMessage DEBUG', { dataType: typeof data, isString: typeof data === 'string', isArrayBuffer: data instanceof ArrayBuffer, dataLength: data?.length || data?.byteLength || 0, - dataConstructor: data?.constructor?.name, - dataSample: typeof data === 'string' ? data.substring(0, 50) : 'not string' }); // ИСПРАВЛЕНИЕ: Проверяем, не является ли это файловым сообщением @@ -2513,7 +2706,7 @@ async processOrderedPackets() { // Файловые сообщения отправляем напрямую без дополнительного шифрования if (parsed.type && parsed.type.startsWith('file_')) { - console.log('📁 Sending file message directly:', parsed.type); + this._secureLog('debug', '📁 Sending file message directly', { type: parsed.type }); this.dataChannel.send(data); return true; } @@ -2530,16 +2723,18 @@ async processOrderedPackets() { timestamp: Date.now() }; - console.log('📤 Sending regular message:', message.data.substring(0, 100)); + this._secureLog('debug', '📤 Sending regular message', { + messageLength: message.data.length, + hasContent: message.data.length > 0 + }); const messageString = JSON.stringify(message); - console.log('📤 ACTUALLY SENDING:', { - messageString: messageString.substring(0, 100), + + this._secureLog('debug', '📤 Message prepared for sending', { messageLength: messageString.length, dataChannelState: this.dataChannel.readyState, isInitiator: this.isInitiator, - isVerified: this.isVerified, - connectionState: this.peerConnection?.connectionState + isVerified: this.isVerified }); this.dataChannel.send(messageString); @@ -2547,13 +2742,16 @@ async processOrderedPackets() { } // Для бинарных данных применяем security layers - console.log('🔐 Applying security layers to non-string data'); + this._secureLog('debug', '🔐 Applying security layers to non-string data'); const securedData = await this.applySecurityLayers(data, false); this.dataChannel.send(securedData); return true; } catch (error) { - console.error('❌ Failed to send message:', error); + this._secureLog('error', '❌ Failed to send message', { + error: error.message, + errorType: error.constructor.name + }); throw error; } } @@ -2759,14 +2957,12 @@ async processOrderedPackets() { notifySecurityUpdate() { try { - if (window.DEBUG_MODE) { - console.log('🔒 Notifying about security level update...', { + this._secureLog('debug', '🔒 Notifying about security level update', { isConnected: this.isConnected(), isVerified: this.isVerified, hasKeys: !!(this.encryptionKey && this.macKey && this.metadataKey), hasLastCalculation: !!this.lastSecurityCalculation }); - } // Send an event about security level update document.dispatchEvent(new CustomEvent('security-level-updated', { @@ -2800,7 +2996,9 @@ notifySecurityUpdate() { } } catch (error) { - console.error('❌ Error in notifySecurityUpdate:', error); + this._secureLog('error', '❌ Error in notifySecurityUpdate', { + error: error.message + }); } } @@ -2995,42 +3193,34 @@ handleSystemMessage(message) { async calculateAndReportSecurityLevel() { try { if (!window.EnhancedSecureCryptoUtils) { - console.warn('⚠️ EnhancedSecureCryptoUtils not available for security calculation'); + this._secureLog('warn', '⚠️ EnhancedSecureCryptoUtils not available for security calculation'); return null; } if (!this.isConnected() || !this.isVerified || !this.encryptionKey || !this.macKey) { - if (window.DEBUG_MODE) { - console.log('⚠️ WebRTC not ready for security calculation:', { - connected: this.isConnected(), - verified: this.isVerified, - hasEncryptionKey: !!this.encryptionKey, - hasMacKey: !!this.macKey - }); - } + this._secureLog('debug', '⚠️ WebRTC not ready for security calculation', { + connected: this.isConnected(), + verified: this.isVerified, + hasEncryptionKey: !!this.encryptionKey, + hasMacKey: !!this.macKey + }); return null; } - if (window.DEBUG_MODE) { - console.log('🔍 Calculating real security level...', { - managerState: 'ready', - encryptionKey: !!this.encryptionKey, - macKey: !!this.macKey, - metadataKey: !!this.metadataKey - }); - } + this._secureLog('debug', '🔍 Calculating real security level', { + managerState: 'ready', + hasAllKeys: !!(this.encryptionKey && this.macKey && this.metadataKey) + }); const securityData = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(this); - if (window.DEBUG_MODE) { - console.log('🔐 Real security level calculated:', { - level: securityData.level, - score: securityData.score, - passedChecks: securityData.passedChecks, - totalChecks: securityData.totalChecks, - isRealData: securityData.isRealData - }); - } + this._secureLog('info', '🔐 Real security level calculated', { + level: securityData.level, + score: securityData.score, + passedChecks: securityData.passedChecks, + totalChecks: securityData.totalChecks, + isRealData: securityData.isRealData + }); this.lastSecurityCalculation = securityData; @@ -3044,7 +3234,6 @@ handleSystemMessage(message) { })); if (securityData.isRealData && this.onMessage) { - // Проверяем, не было ли уже отправлено сообщение о расчете безопасности if (!this.securityCalculationNotificationSent || this.lastSecurityCalculationLevel !== securityData.level) { this.securityCalculationNotificationSent = true; this.lastSecurityCalculationLevel = securityData.level; @@ -3057,7 +3246,10 @@ handleSystemMessage(message) { return securityData; } catch (error) { - console.error('❌ Failed to calculate real security level:', error); + this._secureLog('error', '❌ Failed to calculate real security level', { + error: error.message, + errorType: error.constructor.name + }); return null; } }