fix(NetworkConfigManager, server): enhance IPSec password encryption and decryption error handling; add validation for encrypted password format and improve logging for better debugging
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m21s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m21s
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user