diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index 43c47738..4d6aaf73 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -629,14 +629,7 @@ app.post("/database/restore", async (req, res) => { return res.status(400).json({ error: "Invalid encrypted backup file" }); } - // Check hardware compatibility - if (!DatabaseFileEncryption.validateHardwareCompatibility(backupPath)) { - return res.status(400).json({ - error: "Hardware fingerprint mismatch", - message: - "This backup was created on different hardware and cannot be restored", - }); - } + // Hardware compatibility check removed - no longer required const restoredPath = DatabaseFileEncryption.restoreFromEncryptedBackup( backupPath, diff --git a/src/backend/database/db/index.ts b/src/backend/database/db/index.ts index 7974716e..cb478384 100644 --- a/src/backend/database/db/index.ts +++ b/src/backend/database/db/index.ts @@ -38,21 +38,7 @@ if (enableFileEncryption) { }, ); - // Validate hardware compatibility - if ( - !DatabaseFileEncryption.validateHardwareCompatibility(encryptedDbPath) - ) { - databaseLogger.error( - "Hardware fingerprint mismatch for encrypted database", - { - operation: "db_decrypt_failed", - reason: "hardware_mismatch", - }, - ); - throw new Error( - "Cannot decrypt database: hardware fingerprint mismatch", - ); - } + // Hardware compatibility check removed - using fixed seed encryption // Decrypt database content to memory buffer const decryptedBuffer = diff --git a/src/backend/utils/database-file-encryption.ts b/src/backend/utils/database-file-encryption.ts index 1d2e81c3..c4b3478c 100644 --- a/src/backend/utils/database-file-encryption.ts +++ b/src/backend/utils/database-file-encryption.ts @@ -1,7 +1,6 @@ import crypto from "crypto"; import fs from "fs"; import path from "path"; -import { HardwareFingerprint } from "./hardware-fingerprint.js"; import { databaseLogger } from "./logger.js"; interface EncryptedFileMetadata { @@ -25,13 +24,14 @@ class DatabaseFileEncryption { private static readonly METADATA_FILE_SUFFIX = ".meta"; /** - * Generate file encryption key from hardware fingerprint + * Generate file encryption key from fixed seed (no hardware dependency) */ private static generateFileEncryptionKey(salt: Buffer): Buffer { - const hardwareFingerprint = HardwareFingerprint.generate(); + // Use fixed seed for file encryption - simpler and more reliable than hardware fingerprint + const fixedSeed = process.env.DB_FILE_KEY || "termix-database-file-encryption-seed-v1"; const key = crypto.pbkdf2Sync( - hardwareFingerprint, + fixedSeed, salt, this.KEY_ITERATIONS, 32, // 256 bits for AES-256 @@ -61,7 +61,7 @@ class DatabaseFileEncryption { iv: iv.toString("hex"), tag: tag.toString("hex"), version: this.VERSION, - fingerprint: HardwareFingerprint.generate().substring(0, 16), + fingerprint: "termix-v1-file", // Fixed identifier instead of hardware fingerprint salt: salt.toString("hex"), algorithm: this.ALGORITHM, }; @@ -117,7 +117,7 @@ class DatabaseFileEncryption { iv: iv.toString("hex"), tag: tag.toString("hex"), version: this.VERSION, - fingerprint: HardwareFingerprint.generate().substring(0, 16), + fingerprint: "termix-v1-file", // Fixed identifier instead of hardware fingerprint salt: salt.toString("hex"), algorithm: this.ALGORITHM, }; @@ -173,16 +173,7 @@ class DatabaseFileEncryption { throw new Error(`Unsupported encryption version: ${metadata.version}`); } - // Validate hardware fingerprint - const currentFingerprint = HardwareFingerprint.generate().substring( - 0, - 16, - ); - if (metadata.fingerprint !== currentFingerprint) { - throw new Error( - "Hardware fingerprint mismatch - database was encrypted on different hardware", - ); - } + // Hardware fingerprint validation removed - no longer required // Read encrypted data const encryptedData = fs.readFileSync(encryptedPath); @@ -247,21 +238,7 @@ class DatabaseFileEncryption { throw new Error(`Unsupported encryption version: ${metadata.version}`); } - // Validate hardware fingerprint - const currentFingerprint = HardwareFingerprint.generate().substring( - 0, - 16, - ); - if (metadata.fingerprint !== currentFingerprint) { - databaseLogger.warn("Hardware fingerprint mismatch for database file", { - operation: "database_file_decryption", - expected: metadata.fingerprint, - current: currentFingerprint, - }); - throw new Error( - "Hardware fingerprint mismatch - database was encrypted on different hardware", - ); - } + // Hardware fingerprint validation removed - no longer required // Read encrypted data const encryptedData = fs.readFileSync(encryptedPath); @@ -350,16 +327,13 @@ class DatabaseFileEncryption { const metadata: EncryptedFileMetadata = JSON.parse(metadataContent); const fileStats = fs.statSync(encryptedPath); - const currentFingerprint = HardwareFingerprint.generate().substring( - 0, - 16, - ); + const currentFingerprint = "termix-v1-file"; // Fixed identifier return { version: metadata.version, algorithm: metadata.algorithm, fingerprint: metadata.fingerprint, - isCurrentHardware: metadata.fingerprint === currentFingerprint, + isCurrentHardware: true, // Hardware validation removed fileSize: fileStats.size, }; } catch { @@ -442,14 +416,10 @@ class DatabaseFileEncryption { /** * Validate hardware compatibility for encrypted file + * Always returns true - hardware validation removed */ static validateHardwareCompatibility(encryptedPath: string): boolean { - try { - const info = this.getEncryptedFileInfo(encryptedPath); - return info?.isCurrentHardware ?? false; - } catch { - return false; - } + return true; } /** diff --git a/src/backend/utils/database-migration.ts b/src/backend/utils/database-migration.ts index 7a6c6b82..6a3b620d 100644 --- a/src/backend/utils/database-migration.ts +++ b/src/backend/utils/database-migration.ts @@ -4,7 +4,7 @@ import crypto from "crypto"; import { DatabaseFileEncryption } from "./database-file-encryption.js"; import { DatabaseEncryption } from "./database-encryption.js"; import { FieldEncryption } from "./encryption.js"; -import { HardwareFingerprint } from "./hardware-fingerprint.js"; +// Hardware fingerprint removed - using fixed identifier import { databaseLogger } from "./logger.js"; import { db, databasePaths } from "../database/db/index.js"; import { @@ -23,7 +23,7 @@ interface ExportMetadata { version: string; exportedAt: string; exportId: string; - sourceHardwareFingerprint: string; + sourceIdentifier: string; // Changed from hardware fingerprint tableCount: number; recordCount: number; encryptedFields: string[]; @@ -112,10 +112,7 @@ class DatabaseMigration { version: this.VERSION, exportedAt: timestamp, exportId, - sourceHardwareFingerprint: HardwareFingerprint.generate().substring( - 0, - 16, - ), + sourceIdentifier: "termix-migration-v1", // Fixed identifier tableCount: 0, recordCount: 0, encryptedFields: [], @@ -430,7 +427,7 @@ class DatabaseMigration { const requiredFields = [ "exportedAt", "exportId", - "sourceHardwareFingerprint", + "sourceIdentifier", ]; for (const field of requiredFields) { if (!exportData.metadata[field as keyof ExportMetadata]) { diff --git a/src/backend/utils/database-sqlite-export.ts b/src/backend/utils/database-sqlite-export.ts index 56c3aa7b..8182ac2a 100644 --- a/src/backend/utils/database-sqlite-export.ts +++ b/src/backend/utils/database-sqlite-export.ts @@ -6,7 +6,7 @@ import { sql, eq } from "drizzle-orm"; import { drizzle } from "drizzle-orm/better-sqlite3"; import { DatabaseEncryption } from "./database-encryption.js"; import { FieldEncryption } from "./encryption.js"; -import { HardwareFingerprint } from "./hardware-fingerprint.js"; +// Hardware fingerprint removed - using fixed identifier import { databaseLogger } from "./logger.js"; import { databasePaths, db, sqliteInstance } from "../database/db/index.js"; import { sshData, sshCredentials, users } from "../database/db/schema.js"; @@ -15,7 +15,7 @@ interface ExportMetadata { version: string; exportedAt: string; exportId: string; - sourceHardwareFingerprint: string; + sourceIdentifier: string; // Changed from hardware fingerprint to fixed identifier tableCount: number; recordCount: number; encryptedFields: string[]; @@ -73,10 +73,7 @@ class DatabaseSQLiteExport { version: this.VERSION, exportedAt: timestamp, exportId, - sourceHardwareFingerprint: HardwareFingerprint.generate().substring( - 0, - 16, - ), + sourceIdentifier: "termix-export-v1", // Fixed identifier instead of hardware fingerprint tableCount: 0, recordCount: 0, encryptedFields: [], diff --git a/src/backend/utils/hardware-fingerprint.ts b/src/backend/utils/hardware-fingerprint.ts deleted file mode 100644 index b68201d7..00000000 --- a/src/backend/utils/hardware-fingerprint.ts +++ /dev/null @@ -1,436 +0,0 @@ -import crypto from "crypto"; -import os from "os"; -import { execSync } from "child_process"; -import fs from "fs"; -import { databaseLogger } from "./logger.js"; - -interface HardwareInfo { - cpuId?: string; - motherboardUuid?: string; - diskSerial?: string; - biosSerial?: string; - tpmInfo?: string; - macAddresses?: string[]; -} - -/** - * 硬件指纹生成器 - 使用真实硬件特征生成稳定的设备指纹 - * 相比软件环境指纹,硬件指纹在虚拟化和容器环境中更加稳定 - */ -class HardwareFingerprint { - private static readonly CACHE_KEY = "cached_hardware_fingerprint"; - private static cachedFingerprint: string | null = null; - - /** - * 生成硬件指纹 - * 优先级:缓存 > 环境变量 > 硬件检测 - */ - static generate(): string { - try { - if (this.cachedFingerprint) { - return this.cachedFingerprint; - } - - const envFingerprint = process.env.TERMIX_HARDWARE_SEED; - if (envFingerprint && envFingerprint.length >= 32) { - databaseLogger.info("Using hardware seed from environment variable", { - operation: "hardware_fingerprint_env", - }); - this.cachedFingerprint = this.hashFingerprint(envFingerprint); - return this.cachedFingerprint; - } - - const hwInfo = this.detectHardwareInfo(); - const fingerprint = this.generateFromHardware(hwInfo); - - this.cachedFingerprint = fingerprint; - - return fingerprint; - } catch (error) { - databaseLogger.error("Hardware fingerprint generation failed", error, { - operation: "hardware_fingerprint_failed", - }); - - return this.generateFallbackFingerprint(); - } - } - - /** - * 检测硬件信息 - */ - private static detectHardwareInfo(): HardwareInfo { - const platform = os.platform(); - const hwInfo: HardwareInfo = {}; - - try { - switch (platform) { - case "linux": - hwInfo.cpuId = this.getLinuxCpuId(); - hwInfo.motherboardUuid = this.getLinuxMotherboardUuid(); - hwInfo.diskSerial = this.getLinuxDiskSerial(); - hwInfo.biosSerial = this.getLinuxBiosSerial(); - break; - - case "win32": - hwInfo.cpuId = this.getWindowsCpuId(); - hwInfo.motherboardUuid = this.getWindowsMotherboardUuid(); - hwInfo.diskSerial = this.getWindowsDiskSerial(); - hwInfo.biosSerial = this.getWindowsBiosSerial(); - break; - - case "darwin": - hwInfo.cpuId = this.getMacOSCpuId(); - hwInfo.motherboardUuid = this.getMacOSMotherboardUuid(); - hwInfo.diskSerial = this.getMacOSDiskSerial(); - hwInfo.biosSerial = this.getMacOSBiosSerial(); - break; - } - - // 所有平台都尝试获取MAC地址 - hwInfo.macAddresses = this.getStableMacAddresses(); - } catch (error) { - databaseLogger.error("Some hardware detection failed", error, { - operation: "hardware_detection_partial_failure", - platform, - }); - } - - return hwInfo; - } - - /** - * Linux平台硬件信息获取 - */ - private static getLinuxCpuId(): string | undefined { - try { - // 尝试多种方法获取CPU信息 - const methods = [ - () => - fs - .readFileSync("/proc/cpuinfo", "utf8") - .match(/processor\s*:\s*(\d+)/)?.[1], - () => - execSync('dmidecode -t processor | grep "ID:" | head -1', { - encoding: "utf8", - }).trim(), - () => - execSync( - 'cat /proc/cpuinfo | grep "cpu family\\|model\\|stepping" | md5sum', - { encoding: "utf8" }, - ).split(" ")[0], - ]; - - for (const method of methods) { - try { - const result = method(); - if (result && result.length > 0) return result; - } catch { - /* 继续尝试下一种方法 */ - } - } - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getLinuxMotherboardUuid(): string | undefined { - try { - // 尝试多种方法获取主板UUID - const methods = [ - () => fs.readFileSync("/sys/class/dmi/id/product_uuid", "utf8").trim(), - () => fs.readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim(), - () => execSync("dmidecode -s system-uuid", { encoding: "utf8" }).trim(), - ]; - - for (const method of methods) { - try { - const result = method(); - if (result && result.length > 0 && result !== "Not Settable") - return result; - } catch { - /* 继续尝试下一种方法 */ - } - } - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getLinuxDiskSerial(): string | undefined { - try { - // 获取根分区所在磁盘的序列号 - const rootDisk = execSync( - "df / | tail -1 | awk '{print $1}' | sed 's/[0-9]*$//'", - { encoding: "utf8" }, - ).trim(); - if (rootDisk) { - const serial = execSync( - `udevadm info --name=${rootDisk} | grep ID_SERIAL= | cut -d= -f2`, - { encoding: "utf8" }, - ).trim(); - if (serial && serial.length > 0) return serial; - } - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getLinuxBiosSerial(): string | undefined { - try { - const methods = [ - () => fs.readFileSync("/sys/class/dmi/id/board_serial", "utf8").trim(), - () => - execSync("dmidecode -s baseboard-serial-number", { - encoding: "utf8", - }).trim(), - ]; - - for (const method of methods) { - try { - const result = method(); - if (result && result.length > 0 && result !== "Not Specified") - return result; - } catch { - /* 继续尝试下一种方法 */ - } - } - } catch { - /* 忽略错误 */ - } - return undefined; - } - - /** - * Windows平台硬件信息获取 - */ - private static getWindowsCpuId(): string | undefined { - try { - const result = execSync("wmic cpu get ProcessorId /value", { - encoding: "utf8", - }); - const match = result.match(/ProcessorId=(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getWindowsMotherboardUuid(): string | undefined { - try { - const result = execSync("wmic csproduct get UUID /value", { - encoding: "utf8", - }); - const match = result.match(/UUID=(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getWindowsDiskSerial(): string | undefined { - try { - const result = execSync("wmic diskdrive get SerialNumber /value", { - encoding: "utf8", - }); - const match = result.match(/SerialNumber=(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getWindowsBiosSerial(): string | undefined { - try { - const result = execSync("wmic baseboard get SerialNumber /value", { - encoding: "utf8", - }); - const match = result.match(/SerialNumber=(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - /** - * macOS平台硬件信息获取 - */ - private static getMacOSCpuId(): string | undefined { - try { - const result = execSync("sysctl -n machdep.cpu.brand_string", { - encoding: "utf8", - }); - return result.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getMacOSMotherboardUuid(): string | undefined { - try { - const result = execSync( - 'system_profiler SPHardwareDataType | grep "Hardware UUID"', - { encoding: "utf8" }, - ); - const match = result.match(/Hardware UUID:\s*(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getMacOSDiskSerial(): string | undefined { - try { - const result = execSync( - 'system_profiler SPStorageDataType | grep "Serial Number"', - { encoding: "utf8" }, - ); - const match = result.match(/Serial Number:\s*(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - private static getMacOSBiosSerial(): string | undefined { - try { - const result = execSync( - 'system_profiler SPHardwareDataType | grep "Serial Number"', - { encoding: "utf8" }, - ); - const match = result.match(/Serial Number \(system\):\s*(.+)/); - return match?.[1]?.trim(); - } catch { - /* 忽略错误 */ - } - return undefined; - } - - /** - * 获取稳定的MAC地址 - * 排除虚拟接口和临时接口 - */ - private static getStableMacAddresses(): string[] { - try { - const networkInterfaces = os.networkInterfaces(); - const macAddresses: string[] = []; - - for (const [interfaceName, interfaces] of Object.entries( - networkInterfaces, - )) { - if (!interfaces) continue; - - // 排除虚拟接口和Docker接口 - if (interfaceName.match(/^(lo|docker|veth|br-|virbr)/)) continue; - - for (const iface of interfaces) { - if ( - !iface.internal && - iface.mac && - iface.mac !== "00:00:00:00:00:00" && - !iface.mac.startsWith("02:42:") - ) { - // Docker接口特征 - macAddresses.push(iface.mac); - } - } - } - - return macAddresses.sort(); // 排序确保一致性 - } catch { - return []; - } - } - - /** - * 从硬件信息生成指纹 - */ - private static generateFromHardware(hwInfo: HardwareInfo): string { - const components = [ - hwInfo.motherboardUuid, // 最稳定的标识符 - hwInfo.cpuId, - hwInfo.biosSerial, - hwInfo.diskSerial, - hwInfo.macAddresses?.join(","), - os.platform(), // 操作系统平台 - os.arch(), // CPU架构 - ].filter(Boolean); // 过滤空值 - - if (components.length === 0) { - throw new Error("No hardware identifiers found"); - } - - return this.hashFingerprint(components.join("|")); - } - - /** - * 生成回退指纹(当硬件检测失败时) - */ - private static generateFallbackFingerprint(): string { - const fallbackComponents = [ - os.hostname(), - os.platform(), - os.arch(), - process.cwd(), - "fallback-mode", - ]; - - databaseLogger.warn( - "Using fallback fingerprint due to hardware detection failure", - { - operation: "hardware_fingerprint_fallback", - }, - ); - - return this.hashFingerprint(fallbackComponents.join("|")); - } - - /** - * 标准化指纹哈希 - */ - private static hashFingerprint(data: string): string { - return crypto.createHash("sha256").update(data).digest("hex"); - } - - /** - * 获取硬件指纹信息(用于调试和显示) - */ - static getHardwareInfo(): HardwareInfo & { fingerprint: string } { - const hwInfo = this.detectHardwareInfo(); - return { - ...hwInfo, - fingerprint: this.generate().substring(0, 16), - }; - } - - /** - * 验证当前硬件指纹 - */ - static validateFingerprint(expectedFingerprint: string): boolean { - try { - const currentFingerprint = this.generate(); - return currentFingerprint === expectedFingerprint; - } catch { - return false; - } - } - - /** - * 清除缓存(用于测试) - */ - static clearCache(): void { - this.cachedFingerprint = null; - } -} - -export { HardwareFingerprint }; -export type { HardwareInfo };