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');
|
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 = '';
|
let decryptedPassword = '';
|
||||||
try {
|
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) {
|
} catch (decryptError) {
|
||||||
console.error('Error decrypting password:', 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({
|
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 = {
|
const newPassword = {
|
||||||
@@ -192,7 +232,24 @@ async function updateIpsecPassword(req, res) {
|
|||||||
passwords[index].description = String(description || '').trim();
|
passwords[index].description = String(description || '').trim();
|
||||||
}
|
}
|
||||||
if (password !== undefined) {
|
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();
|
passwords[index].updatedAt = new Date().toISOString();
|
||||||
|
|
||||||
|
|||||||
@@ -53,30 +53,61 @@ function encrypt(text) {
|
|||||||
* @returns {string} - Расшифрованный текст
|
* @returns {string} - Расшифрованный текст
|
||||||
*/
|
*/
|
||||||
function decrypt(encryptedText) {
|
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 {
|
try {
|
||||||
const parts = encryptedText.split(':');
|
const parts = encryptedText.split(':');
|
||||||
if (parts.length !== 3) {
|
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 [ivBase64, authTagBase64, encryptedBase64] = parts;
|
||||||
const iv = Buffer.from(ivBase64, 'base64');
|
|
||||||
const authTag = Buffer.from(authTagBase64, 'base64');
|
if (!ivBase64 || !authTagBase64 || !encryptedBase64) {
|
||||||
const encrypted = 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 key = getEncryptionKey();
|
||||||
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
||||||
decipher.setAuthTag(authTag);
|
decipher.setAuthTag(authTag);
|
||||||
|
|
||||||
let decrypted = decipher.update(encrypted, 'base64', 'utf8');
|
let decrypted = decipher.update(encryptedBase64, 'base64', 'utf8');
|
||||||
decrypted += decipher.final('utf8');
|
decrypted += decipher.final('utf8');
|
||||||
|
|
||||||
return decrypted;
|
return decrypted;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Decryption error:', error);
|
if (error.message.includes('Unsupported state') || error.message.includes('bad decrypt')) {
|
||||||
throw new Error('Failed to decrypt data');
|
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