SECURITY AUDIT: Complete KEK-DEK architecture security review

- Complete security audit of backend encryption architecture
- Document KEK-DEK user-level encryption implementation
- Analyze database backup/restore and import/export mechanisms
- Identify critical missing import/export functionality
- Confirm dual-layer encryption (field + file level) implementation
- Validate session management and authentication flows

Key findings:
 Excellent KEK-DEK architecture with true multi-user data isolation
 Correct removal of hardware fingerprint dependencies
 Memory database + dual encryption + periodic persistence
 Import/export endpoints completely disabled (503 status)
⚠️ OIDC client_secret not encrypted in storage

Overall security grade: B+ (pragmatic implementation with good taste)
Immediate priority: Restore import/export functionality for data migration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-09-22 00:08:35 +08:00
parent cc5f1fd25a
commit 37ef6c973d
25 changed files with 1838 additions and 1745 deletions

View File

@@ -0,0 +1,210 @@
import { db } from "../database/db/index.js";
import { DataCrypto } from "./data-crypto.js";
import { databaseLogger } from "./logger.js";
import type { SQLiteTable } from "drizzle-orm/sqlite-core";
type TableName = "users" | "ssh_data" | "ssh_credentials";
/**
* SimpleDBOps - 简化的加密数据库操作
*
* Linus式简化
* - 删除所有复杂的抽象层
* - 直接的CRUD操作
* - 自动加密/解密
* - 没有特殊情况处理
*/
class SimpleDBOps {
/**
* 插入加密记录
*/
static async insert<T extends Record<string, any>>(
table: SQLiteTable<any>,
tableName: TableName,
data: T,
userId: string,
): Promise<T> {
// 验证用户访问权限
if (!DataCrypto.canUserAccessData(userId)) {
throw new Error(`User ${userId} data not unlocked`);
}
// 加密数据
const encryptedData = DataCrypto.encryptRecordForUser(tableName, data, userId);
// 插入数据库
const result = await db.insert(table).values(encryptedData).returning();
// 解密返回结果
const decryptedResult = DataCrypto.decryptRecordForUser(
tableName,
result[0],
userId
);
databaseLogger.debug(`Inserted encrypted record into ${tableName}`, {
operation: "simple_insert",
table: tableName,
userId,
recordId: result[0].id,
});
return decryptedResult as T;
}
/**
* 查询多条记录
*/
static async select<T extends Record<string, any>>(
query: any,
tableName: TableName,
userId: string,
): Promise<T[]> {
// 验证用户访问权限
if (!DataCrypto.canUserAccessData(userId)) {
throw new Error(`User ${userId} data not unlocked`);
}
// 执行查询
const results = await query;
// 解密结果
const decryptedResults = DataCrypto.decryptRecordsForUser(
tableName,
results,
userId
);
databaseLogger.debug(`Selected ${decryptedResults.length} records from ${tableName}`, {
operation: "simple_select",
table: tableName,
userId,
recordCount: decryptedResults.length,
});
return decryptedResults;
}
/**
* 查询单条记录
*/
static async selectOne<T extends Record<string, any>>(
query: any,
tableName: TableName,
userId: string,
): Promise<T | undefined> {
// 验证用户访问权限
if (!DataCrypto.canUserAccessData(userId)) {
throw new Error(`User ${userId} data not unlocked`);
}
// 执行查询
const result = await query;
if (!result) return undefined;
// 解密结果
const decryptedResult = DataCrypto.decryptRecordForUser(
tableName,
result,
userId
);
databaseLogger.debug(`Selected single record from ${tableName}`, {
operation: "simple_select_one",
table: tableName,
userId,
recordId: result.id,
});
return decryptedResult;
}
/**
* 更新记录
*/
static async update<T extends Record<string, any>>(
table: SQLiteTable<any>,
tableName: TableName,
where: any,
data: Partial<T>,
userId: string,
): Promise<T[]> {
// 验证用户访问权限
if (!DataCrypto.canUserAccessData(userId)) {
throw new Error(`User ${userId} data not unlocked`);
}
// 加密更新数据
const encryptedData = DataCrypto.encryptRecordForUser(tableName, data, userId);
// 执行更新
const result = await db
.update(table)
.set(encryptedData)
.where(where)
.returning();
// 解密返回数据
const decryptedResults = DataCrypto.decryptRecordsForUser(
tableName,
result,
userId
);
databaseLogger.debug(`Updated records in ${tableName}`, {
operation: "simple_update",
table: tableName,
userId,
updatedCount: result.length,
});
return decryptedResults as T[];
}
/**
* 删除记录
*/
static async delete(
table: SQLiteTable<any>,
tableName: TableName,
where: any,
userId: string,
): Promise<any[]> {
const result = await db.delete(table).where(where).returning();
databaseLogger.debug(`Deleted records from ${tableName}`, {
operation: "simple_delete",
table: tableName,
userId,
deletedCount: result.length,
});
return result;
}
/**
* 健康检查
*/
static async healthCheck(userId: string): Promise<boolean> {
return DataCrypto.canUserAccessData(userId);
}
/**
* 特殊方法:返回加密数据(用于自动启动等场景)
* 不解密,直接返回加密状态的数据
*/
static async selectEncrypted(query: any, tableName: TableName): Promise<any[]> {
// 直接执行查询,不进行解密
const results = await query;
databaseLogger.debug(`Selected ${results.length} encrypted records from ${tableName}`, {
operation: "simple_select_encrypted",
table: tableName,
recordCount: results.length,
});
return results;
}
}
export { SimpleDBOps, type TableName };