From 86e8b5feceb5e99b9d1bb3dc486cd4ead6e6de6d Mon Sep 17 00:00:00 2001 From: shats Date: Thu, 22 Jan 2026 19:54:32 +0700 Subject: [PATCH] fix(NetworkConfigManager, server): enhance IPSec password encryption and decryption error handling; add validation for encrypted password format and improve logging for better debugging --- backend/routes/ipsecPasswordsRoutes.js | 65 ++++++++++++++++++++++++-- backend/utils/encryption.js | 47 +++++++++++++++---- 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/backend/routes/ipsecPasswordsRoutes.js b/backend/routes/ipsecPasswordsRoutes.js index cfcd5e4..8cef298 100644 --- a/backend/routes/ipsecPasswordsRoutes.js +++ b/backend/routes/ipsecPasswordsRoutes.js @@ -67,13 +67,37 @@ async function getIpsecPassword(req, res) { return sendError(res, 404, 'IPSec password not found', 'E_NOT_FOUND'); } + // Проверяем наличие зашифрованного пароля + if (!password.encryptedPassword) { + console.error(`IPSec password ${id} has no encryptedPassword field`); + console.error('Password object:', JSON.stringify(password, null, 2)); + return sendError(res, 500, 'Password has no encrypted data', 'E_DECRYPT'); + } + // Расшифровываем пароль для возврата let decryptedPassword = ''; try { - decryptedPassword = decrypt(password.encryptedPassword || ''); + if (typeof password.encryptedPassword !== 'string') { + console.error(`IPSec password ${id} encryptedPassword is not a string:`, typeof password.encryptedPassword); + return sendError(res, 500, 'Invalid encrypted password format', 'E_DECRYPT'); + } + + decryptedPassword = decrypt(password.encryptedPassword); + + if (!decryptedPassword) { + console.error(`IPSec password ${id} decrypted to empty string`); + return sendError(res, 500, 'Password decrypted to empty string', 'E_DECRYPT'); + } } catch (decryptError) { console.error('Error decrypting password:', decryptError); - return sendError(res, 500, 'Error decrypting password', 'E_DECRYPT'); + console.error('Password ID:', id); + console.error('Encrypted password length:', password.encryptedPassword?.length); + console.error('Encrypted password preview:', password.encryptedPassword?.substring(0, 50)); + console.error('Encrypted password format check:', password.encryptedPassword?.split(':').length, 'parts'); + + // Проверяем, возможно это старый формат или другой ключ + const errorMessage = decryptError.message || 'Unknown decryption error'; + return sendError(res, 500, `Error decrypting password: ${errorMessage}. Check if ENCRYPTION_KEY is correct.`, 'E_DECRYPT'); } res.json({ @@ -120,7 +144,23 @@ async function createIpsecPassword(req, res) { } // Шифруем пароль - const encryptedPassword = encrypt(password); + let encryptedPassword; + try { + encryptedPassword = encrypt(password); + if (!encryptedPassword || encryptedPassword.trim() === '') { + console.error('Failed to encrypt password: result is empty'); + return sendError(res, 500, 'Failed to encrypt password', 'E_ENCRYPT'); + } + // Проверяем формат зашифрованного пароля + const parts = encryptedPassword.split(':'); + if (parts.length !== 3) { + console.error('Invalid encrypted password format:', encryptedPassword.substring(0, 50)); + return sendError(res, 500, 'Failed to encrypt password: invalid format', 'E_ENCRYPT'); + } + } catch (encryptError) { + console.error('Error encrypting password:', encryptError); + return sendError(res, 500, `Failed to encrypt password: ${encryptError.message}`, 'E_ENCRYPT'); + } // Создаем новый пароль const newPassword = { @@ -192,7 +232,24 @@ async function updateIpsecPassword(req, res) { passwords[index].description = String(description || '').trim(); } if (password !== undefined) { - passwords[index].encryptedPassword = encrypt(password); + let encryptedPassword; + try { + encryptedPassword = encrypt(password); + if (!encryptedPassword || encryptedPassword.trim() === '') { + console.error('Failed to encrypt password: result is empty'); + return sendError(res, 500, 'Failed to encrypt password', 'E_ENCRYPT'); + } + // Проверяем формат зашифрованного пароля + const parts = encryptedPassword.split(':'); + if (parts.length !== 3) { + console.error('Invalid encrypted password format:', encryptedPassword.substring(0, 50)); + return sendError(res, 500, 'Failed to encrypt password: invalid format', 'E_ENCRYPT'); + } + passwords[index].encryptedPassword = encryptedPassword; + } catch (encryptError) { + console.error('Error encrypting password:', encryptError); + return sendError(res, 500, `Failed to encrypt password: ${encryptError.message}`, 'E_ENCRYPT'); + } } passwords[index].updatedAt = new Date().toISOString(); diff --git a/backend/utils/encryption.js b/backend/utils/encryption.js index ac9b8d9..413d655 100644 --- a/backend/utils/encryption.js +++ b/backend/utils/encryption.js @@ -53,30 +53,61 @@ function encrypt(text) { * @returns {string} - Расшифрованный текст */ function decrypt(encryptedText) { - if (!encryptedText) return ''; + if (!encryptedText) { + throw new Error('Encrypted text is empty'); + } + + if (typeof encryptedText !== 'string') { + throw new Error('Encrypted text must be a string'); + } try { const parts = encryptedText.split(':'); if (parts.length !== 3) { - throw new Error('Invalid encrypted format'); + throw new Error(`Invalid encrypted format: expected 3 parts separated by ':', got ${parts.length}`); } const [ivBase64, authTagBase64, encryptedBase64] = parts; - const iv = Buffer.from(ivBase64, 'base64'); - const authTag = Buffer.from(authTagBase64, 'base64'); - const encrypted = encryptedBase64; + + if (!ivBase64 || !authTagBase64 || !encryptedBase64) { + throw new Error('Invalid encrypted format: one or more parts are empty'); + } + + let iv, authTag; + try { + iv = Buffer.from(ivBase64, 'base64'); + authTag = Buffer.from(authTagBase64, 'base64'); + } catch (bufferError) { + throw new Error(`Failed to decode base64: ${bufferError.message}`); + } + + if (iv.length !== 16) { + throw new Error(`Invalid IV length: expected 16 bytes, got ${iv.length}`); + } + + if (authTag.length !== 16) { + throw new Error(`Invalid auth tag length: expected 16 bytes, got ${authTag.length}`); + } const key = getEncryptionKey(); const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(authTag); - let decrypted = decipher.update(encrypted, 'base64', 'utf8'); + let decrypted = decipher.update(encryptedBase64, 'base64', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } catch (error) { - console.error('Decryption error:', error); - throw new Error('Failed to decrypt data'); + if (error.message.includes('Unsupported state') || error.message.includes('bad decrypt')) { + throw new Error(`Decryption failed: possibly wrong encryption key or corrupted data. Original error: ${error.message}`); + } + console.error('Decryption error details:', { + error: error.message, + stack: error.stack, + encryptedTextLength: encryptedText.length, + encryptedTextPreview: encryptedText.substring(0, 100) + }); + throw error; } }