From 0063e79a73b57afc28de4746b3988f51cf90280c Mon Sep 17 00:00:00 2001 From: shats Date: Tue, 10 Feb 2026 01:54:38 +0700 Subject: [PATCH] feat(mikrotikBackupRoutes): add encryption and decryption for backup content to enhance security --- backend/routes/mikrotikBackupRoutes.js | 36 ++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/backend/routes/mikrotikBackupRoutes.js b/backend/routes/mikrotikBackupRoutes.js index b06b556..9e516c8 100644 --- a/backend/routes/mikrotikBackupRoutes.js +++ b/backend/routes/mikrotikBackupRoutes.js @@ -16,9 +16,24 @@ const { sendError, sendOk } = require('../middleware/errorHandler'); const { writeS3TextObject, readS3TextObject, listS3Objects } = require('../services/s3Service'); const { readServersFromS3 } = require('./serversRoutes'); const { createRosClient, fetchExportViaFile } = require('../services/mikrotikApplyService'); -const { decrypt } = require('../utils/encryption'); +const { encrypt, decrypt } = require('../utils/encryption'); const BACKUP_PREFIX = 'backups/mikrotik'; +const ENCRYPTED_PREFIX = 'ENC:'; + +/** Расшифровать содержимое бэкапа (поддерживает старые незашифрованные) */ +function decryptBackupContent(raw) { + if (!raw || typeof raw !== 'string') return ''; + if (raw.startsWith(ENCRYPTED_PREFIX)) { + try { + return decrypt(raw.slice(ENCRYPTED_PREFIX.length)); + } catch (e) { + console.error('Backup decryption failed:', e?.message); + throw new Error(`Failed to decrypt backup: ${e?.message || 'decryption error'}`); + } + } + return raw; +} function buildBackupKey(serverId, createdAt, suffix) { const safeId = String(serverId || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '_'); @@ -44,7 +59,17 @@ async function saveBackupForServer(serverId, config, { source, comment } = {}) { '', ].filter(Boolean); - const content = `${headerLines.join('\n')}\n${String(config || '').replace(/^\uFEFF/, '')}`; + let content = `${headerLines.join('\n')}\n${String(config || '').replace(/^\uFEFF/, '')}`; + + // Шифруем содержимое на S3, если задан ENCRYPTION_KEY + if (process.env.ENCRYPTION_KEY) { + try { + content = ENCRYPTED_PREFIX + encrypt(content); + } catch (e) { + console.error('Backup encryption failed:', e?.message); + throw new Error(`Failed to encrypt backup: ${e?.message || 'encryption error'}`); + } + } const meta = await writeS3TextObject(key, content, 'text/plain'); return { key, createdAt: now.toISOString(), s3: meta }; @@ -132,11 +157,12 @@ async function getBackup(req, res) { } const data = await readS3TextObject(key); + const config = decryptBackupContent(data.body || ''); return res.json({ ok: true, key, - config: data.body || '', + config, etag: data.etag, lastModified: data.lastModified, contentLength: data.contentLength, @@ -169,8 +195,8 @@ async function diffBackups(req, res) { readS3TextObject(keyB), ]); - const bodyA = a.body || ''; - const bodyB = b.body || ''; + const bodyA = decryptBackupContent(a.body || ''); + const bodyB = decryptBackupContent(b.body || ''); return res.json({ ok: true,