feat: 初始化项目结构并添加核心功能模块
- 新增文档模板和导航结构 - 实现服务器基础API路由和控制器 - 添加扩展插件配置和前端框架 - 引入多租户和权限管理模块 - 集成日志和数据库配置 - 添加核心业务模型和类型定义
This commit is contained in:
88
server/src/core/security/AGIKillSwitchService.ts
Normal file
88
server/src/core/security/AGIKillSwitchService.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
import db from '../../config/database';
|
||||
import { HardwareEnclaveService } from './HardwareEnclaveService';
|
||||
|
||||
export interface KillSwitchStatus {
|
||||
tenantId: string;
|
||||
isActivated: boolean;
|
||||
reason?: string;
|
||||
activatedAt?: Date;
|
||||
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_21] 针对 AGI 恶意演化的硬核熔断机制 (AGI Kill-Switch)
|
||||
* @description 核心逻辑:建立物理/逻辑级隔离的 AI 行为准则门禁。
|
||||
* 当检测到 AGI 代理出现非预期演化(如绕过安全限制、尝试非法越权、产生严重幻觉)时,
|
||||
* 能够瞬间强制关停该租户或全局的 AGI 推理引擎。
|
||||
*/
|
||||
export class AGIKillSwitchService {
|
||||
private static readonly STATUS_TABLE = 'cf_agi_kill_switches';
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.STATUS_TABLE);
|
||||
if (!hasTable) {
|
||||
console.log(`📦 Creating ${this.STATUS_TABLE} table...`);
|
||||
await db.schema.createTable(this.STATUS_TABLE, (table) => {
|
||||
table.string('tenant_id', 64).primary();
|
||||
table.boolean('is_activated').defaultTo(false);
|
||||
table.string('reason', 256);
|
||||
table.string('severity', 16);
|
||||
table.timestamp('activated_at');
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
console.log(`✅ Table ${this.STATUS_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活熔断机制
|
||||
*/
|
||||
static async activate(tenantId: string, reason: string, severity: KillSwitchStatus['severity'] = 'HIGH') {
|
||||
logger.error(`[AGIKillSwitch] ACTIVATING KILL-SWITCH for Tenant: ${tenantId}. Reason: ${reason}`);
|
||||
|
||||
await db(this.STATUS_TABLE)
|
||||
.insert({
|
||||
tenant_id: tenantId,
|
||||
is_activated: true,
|
||||
reason,
|
||||
severity,
|
||||
activated_at: new Date()
|
||||
})
|
||||
.onConflict('tenant_id')
|
||||
.merge();
|
||||
|
||||
// 联动 TEE 硬件隔离层进行物理级隔离 (模拟)
|
||||
await HardwareEnclaveService.runProtectedTask('AGI_HARD_STOP', { tenantId }, {
|
||||
tenantId,
|
||||
enclaveType: 'INTEL_SGX',
|
||||
measurementHash: 'AGI_KILL_SIG_0x7f'
|
||||
});
|
||||
|
||||
// 异步清除相关 AGI 节点的缓存与运行状态
|
||||
// await AgentSwarmService.broadcastKillSignal(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查租户是否已被熔断
|
||||
*/
|
||||
static async isBroken(tenantId: string): Promise<boolean> {
|
||||
const status = await db(this.STATUS_TABLE).where({ tenant_id: tenantId }).first();
|
||||
return status?.is_activated || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除熔断 (需要高级管理员权限)
|
||||
*/
|
||||
static async deactivate(tenantId: string) {
|
||||
await db(this.STATUS_TABLE)
|
||||
.where({ tenant_id: tenantId })
|
||||
.update({ is_activated: false, reason: null, severity: null, activated_at: null });
|
||||
|
||||
logger.info(`[AGIKillSwitch] Kill-switch deactivated for Tenant: ${tenantId}`);
|
||||
}
|
||||
}
|
||||
87
server/src/core/security/ActionAuditService.ts
Normal file
87
server/src/core/security/ActionAuditService.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { SemanticLogService } from '../telemetry/SemanticLogService';
|
||||
|
||||
export interface SensitiveAction {
|
||||
tenantId: string;
|
||||
userId: string;
|
||||
actionType: string;
|
||||
resourceId: string;
|
||||
payload: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* [BIZ_KER_133] 租户敏感操作二次确认审计 (Action Audit)
|
||||
* @description 核心逻辑:拦截并审计租户的敏感操作(如删除核心配置、大额退款等)。
|
||||
* 强制执行二次确认逻辑(配合前端 UI),并记录完整的操作轨迹与 AGI 风险评估。
|
||||
* 联动语义日志中心输出主权安全审计报告。
|
||||
* 遵循 Autocomplete-First (V31.5) 规范。
|
||||
*/
|
||||
export class ActionAuditService {
|
||||
private static readonly AUDIT_TABLE = 'cf_sensitive_action_logs';
|
||||
|
||||
/**
|
||||
* 初始化审计表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.AUDIT_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.AUDIT_TABLE} table...`);
|
||||
await db.schema.createTable(this.AUDIT_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('user_id', 64).notNullable();
|
||||
table.string('action_type', 64).notNullable();
|
||||
table.string('resource_id', 128);
|
||||
table.json('payload');
|
||||
table.string('risk_level', 16).defaultTo('LOW');
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
table.index(['tenant_id', 'action_type']);
|
||||
});
|
||||
logger.info(`✅ Table ${this.AUDIT_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录并评估敏感操作
|
||||
* @param action 操作详情
|
||||
*/
|
||||
static async auditAction(action: SensitiveAction) {
|
||||
logger.info(`[ActionAudit] Auditing sensitive action: ${action.actionType} for Tenant ${action.tenantId}`);
|
||||
|
||||
// 1. AGI 风险评估 (模拟)
|
||||
const riskLevel = this.evaluateRisk(action);
|
||||
|
||||
// 2. 存储审计日志
|
||||
await db(this.AUDIT_TABLE).insert({
|
||||
tenant_id: action.tenantId,
|
||||
user_id: action.userId,
|
||||
action_type: action.actionType,
|
||||
resource_id: action.resourceId,
|
||||
payload: JSON.stringify(action.payload),
|
||||
risk_level: riskLevel
|
||||
});
|
||||
|
||||
// 3. 针对高风险操作输出语义日志报告
|
||||
if (riskLevel === 'HIGH' || riskLevel === 'CRITICAL') {
|
||||
const report = this.generateReport(action, riskLevel);
|
||||
await SemanticLogService.logSemantic(report, 'ERROR', 'SOVEREIGN_SECURITY');
|
||||
}
|
||||
}
|
||||
|
||||
private static evaluateRisk(action: SensitiveAction): string {
|
||||
const highRiskTypes = ['DELETE_TENANT', 'BULK_REFUND', 'CHANGE_MASTER_KEY'];
|
||||
if (highRiskTypes.includes(action.actionType)) return 'CRITICAL';
|
||||
if (action.actionType.includes('DELETE') || action.actionType.includes('UPDATE_CONFIG')) return 'HIGH';
|
||||
return 'LOW';
|
||||
}
|
||||
|
||||
private static generateReport(action: SensitiveAction, risk: string): string {
|
||||
return `### 🛡️ Sovereign Security: High-Risk Action Detected\n\n` +
|
||||
`**Tenant:** ${action.tenantId} | **User:** ${action.userId}\n` +
|
||||
`**Action:** \`${action.actionType}\` | **Resource:** \`${action.resourceId}\`\n` +
|
||||
`**Risk Level:** [ ${risk} ]\n\n` +
|
||||
`**Payload Snapshot:** \`${JSON.stringify(action.payload)}\`\n\n` +
|
||||
`**Recommendation:** 此操作涉及主权安全或核心资产变更,请确保已通过人工二次确认流程。`;
|
||||
}
|
||||
}
|
||||
174
server/src/core/security/AgentTraceAuditService.ts
Normal file
174
server/src/core/security/AgentTraceAuditService.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
import db from '../../config/database';
|
||||
import { ExplainableAIService } from '../ai/ExplainableAIService';
|
||||
import { BehavioralRiskService } from '../governance/BehavioralRiskService';
|
||||
|
||||
export interface AgentTraceAudit {
|
||||
id?: number;
|
||||
agentId: string;
|
||||
tenantId: string;
|
||||
taskId: string;
|
||||
tracePath: string[]; // 行为路径 (节点序列)
|
||||
complianceScore: number; // 合规分 (0-100)
|
||||
violationType?: string;
|
||||
auditEvidence: string; // 证据指纹
|
||||
status: 'PENDING' | 'AUDITED' | 'REJECTED';
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [BIZ_AUDIT_14] 基于 AI 代理行为轨迹的合规溯源 (Agent Trace Audit)
|
||||
* @description 核心逻辑:提供对 AGI 代理行为轨迹的自动化合规审计与证据存证。
|
||||
* 审计系统不仅记录 AGI 做了什么,还利用 XAI 技术记录其决策理由(Reasoning),
|
||||
* 确保在发生合规争议(如:违反反垄断法、低价倾销)时,
|
||||
* 能够进行因果链路还原与责任界定。
|
||||
*/
|
||||
export class AgentTraceAuditService {
|
||||
private static readonly AUDIT_TABLE = 'cf_agent_trace_audits';
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.AUDIT_TABLE);
|
||||
if (!hasTable) {
|
||||
console.log(`📦 Creating ${this.AUDIT_TABLE} table...`);
|
||||
await db.schema.createTable(this.AUDIT_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('agent_id', 64).notNullable();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('task_id', 64).notNullable();
|
||||
table.json('trace_path');
|
||||
table.integer('compliance_score').defaultTo(100);
|
||||
table.string('violation_type', 64);
|
||||
table.text('audit_evidence');
|
||||
table.string('status', 16).defaultTo('PENDING');
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
table.index(['agent_id', 'tenant_id', 'task_id', 'status']);
|
||||
});
|
||||
console.log(`✅ Table ${this.AUDIT_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交代理行为轨迹进行审计 (BIZ_AUDIT_AGENT_TRACE)
|
||||
* @description 联动 [ExplainableAIService] 获取决策证据,实现全量审计溯源。
|
||||
*/
|
||||
static async auditTrace(params: {
|
||||
agentId: string;
|
||||
tenantId: string;
|
||||
taskId: string;
|
||||
tracePath: string[];
|
||||
decisionId?: string; // 关联的决策 ID
|
||||
evidence: any;
|
||||
}): Promise<AgentTraceAudit | null> {
|
||||
// Feature Flag Check
|
||||
if (!(await FeatureGovernanceService.isEnabled('BIZ_AUDIT_AGENT_TRACE', params.tenantId))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info(`[AgentTraceAudit] Auditing trace for Agent ${params.agentId} on Task ${params.taskId}`);
|
||||
|
||||
// 1. 获取 AI 决策证据 (联动 [ExplainableAIService])
|
||||
let reasoning = 'No explicit reasoning found.';
|
||||
let decisionDetails: any = null;
|
||||
if (params.decisionId) {
|
||||
const explanation = await ExplainableAIService.getExplanation(params.decisionId, params.tenantId);
|
||||
reasoning = explanation?.explanation?.logic || reasoning;
|
||||
decisionDetails = explanation?.decision;
|
||||
}
|
||||
|
||||
// 2. 生产级合规性校验 (Zero-Mock)
|
||||
const auditResult = await this.validateCompliance(params.tracePath, decisionDetails);
|
||||
const score = auditResult.isCompliant ? 100 : auditResult.score;
|
||||
const violationType = auditResult.violationType;
|
||||
|
||||
const record: AgentTraceAudit = {
|
||||
agentId: params.agentId,
|
||||
tenantId: params.tenantId,
|
||||
taskId: params.taskId,
|
||||
tracePath: params.tracePath,
|
||||
complianceScore: score,
|
||||
violationType: violationType as any,
|
||||
auditEvidence: JSON.stringify({
|
||||
...params.evidence,
|
||||
reasoning,
|
||||
complianceDetail: auditResult.detail
|
||||
}),
|
||||
status: score < 60 ? 'REJECTED' : 'AUDITED',
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
// 3. 存储审计记录
|
||||
const [id] = await db(this.AUDIT_TABLE).insert({
|
||||
agent_id: record.agentId,
|
||||
tenant_id: record.tenantId,
|
||||
task_id: record.taskId,
|
||||
trace_path: JSON.stringify(record.tracePath),
|
||||
compliance_score: record.complianceScore,
|
||||
violation_type: record.violationType,
|
||||
audit_evidence: record.auditEvidence,
|
||||
status: record.status
|
||||
});
|
||||
|
||||
record.id = id;
|
||||
|
||||
// 4. 联动风险评分系统
|
||||
if (score < 60) {
|
||||
await BehavioralRiskService.updateRisk({
|
||||
tenantId: params.tenantId,
|
||||
anomaly: `Agent trace violation: ${violationType} (Score: ${score})`,
|
||||
impact: 100 - score
|
||||
});
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产级合规性验证逻辑 (V30.0)
|
||||
*/
|
||||
private static async validateCompliance(tracePath: string[], decision: any): Promise<{
|
||||
isCompliant: boolean;
|
||||
score: number;
|
||||
violationType?: string;
|
||||
detail?: string;
|
||||
}> {
|
||||
// 1. 路径深度审计 (防止死循环或算力滥用)
|
||||
if (tracePath.length > 100) {
|
||||
return { isCompliant: false, score: 30, violationType: 'PATH_DEPTH_EXCEEDED', detail: 'Agent execution path too long (>100 steps)' };
|
||||
}
|
||||
|
||||
// 2. 敏感操作审计 (若包含 DELETE 或 TRUNCATE 关键词)
|
||||
const highRiskActions = tracePath.filter(step => /delete|truncate|drop/i.test(step));
|
||||
if (highRiskActions.length > 0) {
|
||||
return { isCompliant: false, score: 0, violationType: 'HIGH_RISK_COMMAND', detail: `Detected unauthorized destructive commands: ${highRiskActions.join(', ')}` };
|
||||
}
|
||||
|
||||
// 3. 业务红线审计 (联动 Project Rules)
|
||||
if (decision && decision.module === 'PRICING') {
|
||||
const { newPrice, cost, type } = decision; // type: 'B2B' | 'B2C'
|
||||
const margin = (newPrice - cost) / newPrice;
|
||||
|
||||
if (type === 'B2B' && margin < 0.15) {
|
||||
return { isCompliant: false, score: 10, violationType: 'MARGIN_REDLINE_BREACH', detail: `B2B Margin (${(margin * 100).toFixed(2)}%) below 15% redline.` };
|
||||
}
|
||||
if (type === 'B2C' && margin < 0.20) {
|
||||
return { isCompliant: false, score: 50, violationType: 'MARGIN_WARNING', detail: `B2C Margin (${(margin * 100).toFixed(2)}%) below 20% warning threshold.` };
|
||||
}
|
||||
}
|
||||
|
||||
return { isCompliant: true, score: 100 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近的违规审计报告
|
||||
*/
|
||||
static async getViolationReports(limit: number = 10) {
|
||||
return db(this.AUDIT_TABLE)
|
||||
.where('compliance_score', '<', 60)
|
||||
.orderBy('created_at', 'desc')
|
||||
.limit(limit);
|
||||
}
|
||||
}
|
||||
89
server/src/core/security/CacheConsistencyService.ts
Normal file
89
server/src/core/security/CacheConsistencyService.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { SemanticLogService } from '../telemetry/SemanticLogService';
|
||||
import crypto from 'crypto';
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* [BIZ_KER_121] 静态资源缓存一致性校验 (Cache Sync)
|
||||
* @description 核心逻辑:定期对比源站与 CDN 节点的静态资源(JS/CSS/Images)哈希值。
|
||||
* 识别因 CDN 刷新延迟或运营商劫持导致的“脏缓存”问题。
|
||||
* 联动语义日志中心输出一致性报告,并对不匹配资源进行预警。
|
||||
* 遵循 Autocomplete-First (V31.5) 规范。
|
||||
*/
|
||||
export class CacheConsistencyService {
|
||||
private static readonly RESOURCE_LIST = [
|
||||
'/static/js/main.js',
|
||||
'/static/css/theme.css'
|
||||
];
|
||||
private static readonly CDN_ENDPOINTS = [
|
||||
'https://cdn1.crawlful.com',
|
||||
'https://cdn2.crawlful.com'
|
||||
];
|
||||
|
||||
/**
|
||||
* 初始化缓存校验任务
|
||||
*/
|
||||
static async init() {
|
||||
logger.info(`[CacheConsistency] Initializing CDN cache sync monitor...`);
|
||||
this.runCheck();
|
||||
setInterval(() => this.runCheck(), 3600000); // 每小时校验一次
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行一致性校验
|
||||
* @private
|
||||
*/
|
||||
private static async runCheck() {
|
||||
const report: string[] = [];
|
||||
let hasIssue = false;
|
||||
|
||||
for (const resource of this.RESOURCE_LIST) {
|
||||
try {
|
||||
// 1. 获取源站哈希 (模拟从本地文件读取或 Origin 请求)
|
||||
const originHash = await this.getResourceHash(`https://origin.crawlful.com${resource}`);
|
||||
|
||||
// 2. 对比各 CDN 节点
|
||||
for (const cdn of this.CDN_ENDPOINTS) {
|
||||
const cdnHash = await this.getResourceHash(`${cdn}${resource}`);
|
||||
|
||||
if (originHash !== cdnHash) {
|
||||
hasIssue = true;
|
||||
report.push(`❌ Mismatch: \`${resource}\` on \`${cdn}\` (Expected: ${originHash.substring(0, 8)}, Got: ${cdnHash.substring(0, 8)})`);
|
||||
} else {
|
||||
report.push(`✅ Match: \`${resource}\` on \`${cdn}\``);
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
logger.error(`[CacheConsistency] Failed to check ${resource}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const mdReport = this.generateMarkdownReport(report, hasIssue);
|
||||
await SemanticLogService.logSemantic(mdReport, hasIssue ? 'ERROR' : 'INFO', 'CACHE_AUDIT');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程资源的 MD5 哈希
|
||||
* @private
|
||||
*/
|
||||
private static async getResourceHash(url: string): Promise<string> {
|
||||
try {
|
||||
const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 5000 });
|
||||
return crypto.createHash('md5').update(Buffer.from(response.data)).digest('hex');
|
||||
} catch (err) {
|
||||
return 'FETCH_FAILED';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Markdown 报告
|
||||
* @private
|
||||
*/
|
||||
private static generateMarkdownReport(results: string[], hasIssue: boolean): string {
|
||||
return `### 🌐 CDN Cache Consistency Audit\n\n` +
|
||||
`**Status:** ${hasIssue ? '🚨 Issues Detected' : '✅ All Synchronized'}\n\n` +
|
||||
`**Audit Details:**\n` +
|
||||
results.map(r => `- ${r}`).join('\n') +
|
||||
(hasIssue ? `\n\n**Recommendation:** 请立即执行 CDN 全量刷新,或核查源站发布流水线是否同步完成。` : '');
|
||||
}
|
||||
}
|
||||
87
server/src/core/security/CertsMonitorService.ts
Normal file
87
server/src/core/security/CertsMonitorService.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { LogMaskingService } from '../security/LogMaskingService';
|
||||
import { SemanticLogService } from '../telemetry/SemanticLogService';
|
||||
import https from 'https';
|
||||
|
||||
/**
|
||||
* [BIZ_INF_106] 租户域名证书过期自动预警 (SSL Watch)
|
||||
* @description 核心逻辑:定时扫描租户配置的域名,获取其 SSL 证书到期时间。
|
||||
* 当到期时间小于 15 天时,自动生成预警并归档至语义日志中心。
|
||||
* 通过 [LogMaskingService] 屏蔽域名 PII。
|
||||
*/
|
||||
export class CertsMonitorService {
|
||||
private static readonly ALERT_THRESHOLD_DAYS = 15; // 预警阈值:15天
|
||||
private static readonly CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 检查间隔:24小时
|
||||
|
||||
/**
|
||||
* 初始化定时任务
|
||||
*/
|
||||
static async init() {
|
||||
this.runCheck();
|
||||
setInterval(() => this.runCheck(), this.CHECK_INTERVAL_MS);
|
||||
logger.info(`[SSLWatch] Certificate monitor initialized (Alert threshold: ${this.ALERT_THRESHOLD_DAYS} days)`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行证书扫描
|
||||
*/
|
||||
private static async runCheck() {
|
||||
try {
|
||||
// 1. 获取所有活跃租户的自定义域名 (模拟从 cf_tenants 表中获取)
|
||||
const tenants = await db('cf_tenants').select('id', 'custom_domain').whereNotNull('custom_domain');
|
||||
|
||||
for (const tenant of tenants) {
|
||||
if (!tenant.custom_domain) continue;
|
||||
|
||||
const expirationDate = await this.getCertificateExpirationDate(tenant.custom_domain);
|
||||
if (!expirationDate) continue;
|
||||
|
||||
const daysRemaining = Math.ceil((expirationDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (daysRemaining <= this.ALERT_THRESHOLD_DAYS) {
|
||||
await this.reportExpiration(tenant.id, tenant.custom_domain, daysRemaining, expirationDate);
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
logger.error(`[SSLWatch] Scan failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取证书过期日期 (通过 HTTPS 请求获取证书)
|
||||
*/
|
||||
private static async getCertificateExpirationDate(domain: string): Promise<Date | null> {
|
||||
return new Promise((resolve) => {
|
||||
const options = {
|
||||
hostname: domain,
|
||||
port: 443,
|
||||
method: 'GET',
|
||||
rejectUnauthorized: false, // 允许自签名或过期证书以便检测
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
const cert = (res.socket as any).getPeerCertificate();
|
||||
if (cert && cert.valid_to) {
|
||||
resolve(new Date(cert.valid_to));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
req.on('error', () => resolve(null));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报预警信息
|
||||
*/
|
||||
private static async reportExpiration(tenantId: string, domain: string, daysRemaining: number, expirationDate: Date) {
|
||||
const maskedDomain = LogMaskingService.maskData(domain);
|
||||
const message = `🚨 SSL Certificate Expiration Warning: Domain \`${maskedDomain}\` (Tenant: ${tenantId}) will expire in ${daysRemaining} days (on ${expirationDate.toISOString()}). Please renew the certificate immediately.`;
|
||||
|
||||
await SemanticLogService.logSemantic(message, 'ERROR', 'SECURITY_MONITOR');
|
||||
logger.error(`[SSLWatch] Certificate for domain ${maskedDomain} is expiring soon.`);
|
||||
}
|
||||
}
|
||||
73
server/src/core/security/ContainerSecurityService.ts
Normal file
73
server/src/core/security/ContainerSecurityService.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { SemanticLogService } from '../telemetry/SemanticLogService';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* [BIZ_KER_125] 自动化 Dockerfile 漏洞扫描建议 (Docker Sec)
|
||||
* @description 核心逻辑:扫描项目中 Dockerfile 及镜像配置。
|
||||
* 识别不安全的镜像基础层、未受限的 root 权限、不必要的敏感文件拷贝等风险。
|
||||
* 联动语义日志中心输出 Docker 安全审计报告,并提供优化建议。
|
||||
* 遵循 Autocomplete-First (V31.5) 规范。
|
||||
*/
|
||||
export class ContainerSecurityService {
|
||||
private static readonly DOCKERFILE_PATH = path.join(process.cwd(), 'Dockerfile');
|
||||
|
||||
/**
|
||||
* 初始化容器安全扫描
|
||||
*/
|
||||
static async init() {
|
||||
logger.info(`[DockerSec] Initializing container security scanner...`);
|
||||
this.runScan();
|
||||
setInterval(() => this.runScan(), 86400000); // 每日扫描一次
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 Dockerfile 静态分析
|
||||
* @private
|
||||
*/
|
||||
private static async runScan() {
|
||||
try {
|
||||
if (!fs.existsSync(this.DOCKERFILE_PATH)) {
|
||||
logger.warn(`[DockerSec] Dockerfile not found at ${this.DOCKERFILE_PATH}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(this.DOCKERFILE_PATH, 'utf8');
|
||||
const findings: string[] = [];
|
||||
|
||||
// 1. 识别不安全的镜像基础层 (FROM node:latest 或 alpine)
|
||||
if (content.includes('FROM node:latest')) {
|
||||
findings.push(`⚠️ 使用了 \`node:latest\` 基础镜像。建议锁定具体版本号 (如 \`node:20-alpine\`) 以保证构建可追溯。`);
|
||||
}
|
||||
|
||||
// 2. 识别未受限的 root 权限 (没有 USER 指令)
|
||||
if (!content.includes('USER ')) {
|
||||
findings.push(`❌ Dockerfile 中缺少 \`USER\` 指令。容器将默认以 root 身份运行,存在越权风险。`);
|
||||
}
|
||||
|
||||
// 3. 识别不必要的敏感文件拷贝 (COPY . .)
|
||||
if (content.includes('COPY . .')) {
|
||||
findings.push(`💡 识别到 \`COPY . .\`。请确保已配置 \`.dockerignore\` 以防止 \`.env\` 或 \`.git\` 等敏感目录进入镜像。`);
|
||||
}
|
||||
|
||||
const report = this.generateMarkdownReport(findings);
|
||||
await SemanticLogService.logSemantic(report, findings.length > 0 ? 'WARN' : 'INFO', 'DOCKER_SECURITY');
|
||||
|
||||
} catch (err: any) {
|
||||
logger.error(`[DockerSec] Scan failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Markdown 审计报告
|
||||
* @private
|
||||
*/
|
||||
private static generateMarkdownReport(findings: string[]): string {
|
||||
return `### 🐳 Container & Docker Security Audit\n\n` +
|
||||
`**Status:** ${findings.length > 0 ? '🚨 Security Risks Identified' : '✅ Compliant'}\n\n` +
|
||||
`**Audit Findings:**\n` +
|
||||
(findings.length > 0 ? findings.map(f => `- ${f}`).join('\n') : '- No critical Dockerfile risks detected.') +
|
||||
(findings.length > 0 ? `\n\n**Recommendation:** 请根据上述审计建议优化 Dockerfile 配置,提升容器运行时的安全性。` : '');
|
||||
}
|
||||
}
|
||||
90
server/src/core/security/ContinuousAuthService.ts
Normal file
90
server/src/core/security/ContinuousAuthService.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import * as crypto from 'crypto';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { RedisService } from '../../utils/RedisService';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
|
||||
export interface AuthEvent {
|
||||
nodeId: string;
|
||||
ip: string;
|
||||
behaviorScore: number; // 0-1
|
||||
lastVerifiedAt: Date;
|
||||
status: 'VERIFIED' | 'SUSPICIOUS' | 'REVOKED';
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_11] 零信任节点身份连续认证 (Continuous Auth)
|
||||
* @description 核心逻辑:基于节点行为特征(请求频率、地理位置、业务模式)进行持续安全校验,而非仅在登录时校验。
|
||||
*/
|
||||
export class ContinuousAuthService {
|
||||
private static readonly AUTH_PREFIX = 'sec:auth:node:';
|
||||
|
||||
/**
|
||||
* 持续身份校验 (Continuous Validation)
|
||||
*/
|
||||
static async validateNode(nodeId: string, context: { ip: string; action: string; metadata?: any }): Promise<boolean> {
|
||||
// Feature Flag Check
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_CONTINUOUS_AUTH'))) {
|
||||
return true; // 如果关闭,则回退到基础 JWT 校验
|
||||
}
|
||||
|
||||
const authKey = `${this.AUTH_PREFIX}${nodeId}`;
|
||||
const cachedAuth = await RedisService.get(authKey);
|
||||
let event: AuthEvent;
|
||||
|
||||
if (cachedAuth) {
|
||||
event = JSON.parse(cachedAuth);
|
||||
} else {
|
||||
event = {
|
||||
nodeId,
|
||||
ip: context.ip,
|
||||
behaviorScore: 1.0,
|
||||
lastVerifiedAt: new Date(),
|
||||
status: 'VERIFIED'
|
||||
};
|
||||
}
|
||||
|
||||
// 行为风险评估算法 (Behavioral Risk Assessment)
|
||||
const riskScore = this.calculateRisk(event, context);
|
||||
event.behaviorScore = Math.max(0, event.behaviorScore - riskScore);
|
||||
event.lastVerifiedAt = new Date();
|
||||
|
||||
// 如果评分低于阈值,标记为 SUSPICIOUS 或 REVOKED
|
||||
if (event.behaviorScore < 0.3) {
|
||||
event.status = 'REVOKED';
|
||||
logger.error(`[ContinuousAuth] Security Breach! Node ${nodeId} status REVOKED. Score: ${event.behaviorScore}`);
|
||||
} else if (event.behaviorScore < 0.7) {
|
||||
event.status = 'SUSPICIOUS';
|
||||
logger.warn(`[ContinuousAuth] Suspicious behavior from Node ${nodeId}. Score: ${event.behaviorScore}`);
|
||||
}
|
||||
|
||||
await RedisService.set(authKey, JSON.stringify(event), 3600);
|
||||
return event.status !== 'REVOKED';
|
||||
}
|
||||
|
||||
/**
|
||||
* 风险计算逻辑
|
||||
*/
|
||||
private static calculateRisk(event: AuthEvent, context: { ip: string; action: string }): number {
|
||||
let risk = 0;
|
||||
|
||||
// 1. IP 变更风险
|
||||
if (event.ip !== context.ip) risk += 0.3;
|
||||
|
||||
// 2. 异常动作风险 (如短时间内大量删除)
|
||||
if (context.action === 'MASS_DELETE') risk += 0.5;
|
||||
|
||||
// 3. 时间窗口频率检查 (模拟)
|
||||
const isLateNight = new Date().getHours() < 5;
|
||||
if (isLateNight) risk += 0.1;
|
||||
|
||||
return risk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成动态行为令牌
|
||||
*/
|
||||
static async generateBehaviorToken(nodeId: string): Promise<string> {
|
||||
const salt = crypto.randomBytes(16).toString('hex');
|
||||
return crypto.createHash('sha256').update(nodeId + salt + Date.now()).digest('hex');
|
||||
}
|
||||
}
|
||||
151
server/src/core/security/DIDHandshakeService.ts
Normal file
151
server/src/core/security/DIDHandshakeService.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
import db from '../../config/database';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
export interface HandshakeSession {
|
||||
sessionId: string;
|
||||
sourceTenantId: string;
|
||||
targetTenantId: string;
|
||||
sourceDid: string;
|
||||
targetDid: string;
|
||||
status: 'INITIATED' | 'VERIFIED' | 'EXPIRED' | 'REVOKED';
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_16] 基于去中心化身份的跨租户安全握手 (DID Handshake)
|
||||
* @description 核心逻辑:实现基于 W3C DID 标准的租户间安全握手协议。
|
||||
* 允许租户在不依赖中心化 CA 的情况下,通过去中心化身份进行双向认证与安全会话建立,
|
||||
* 支持跨租户的数据交换(如声誉共享、协同采购)。
|
||||
*/
|
||||
export class DIDHandshakeService {
|
||||
private static readonly SESSION_TABLE = 'cf_did_handshake_sessions';
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.SESSION_TABLE);
|
||||
if (!hasTable) {
|
||||
console.log(`📦 Creating ${this.SESSION_TABLE} table...`);
|
||||
await db.schema.createTable(this.SESSION_TABLE, (table) => {
|
||||
table.string('session_id', 64).primary();
|
||||
table.string('source_tenant_id', 64).notNullable();
|
||||
table.string('target_tenant_id', 64).notNullable();
|
||||
table.string('source_did', 128).notNullable();
|
||||
table.string('target_did', 128).notNullable();
|
||||
table.string('status', 16).defaultTo('INITIATED');
|
||||
table.text('proof_payload');
|
||||
table.timestamp('expires_at').notNullable();
|
||||
table.timestamps(true, true);
|
||||
table.index(['source_tenant_id', 'target_tenant_id'], 'idx_did_handshake_tenants');
|
||||
});
|
||||
console.log(`✅ Table ${this.SESSION_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起握手请求
|
||||
*/
|
||||
static async initiateHandshake(params: {
|
||||
sourceTenantId: string;
|
||||
targetTenantId: string;
|
||||
sourceDid: string;
|
||||
targetDid: string;
|
||||
}): Promise<string> {
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_DID_HANDSHAKE', params.sourceTenantId))) {
|
||||
throw new Error('DID Handshake feature is disabled');
|
||||
}
|
||||
|
||||
const sessionId = crypto.randomBytes(32).toString('hex');
|
||||
const expiresAt = new Date(Date.now() + 3600 * 1000); // 1小时有效
|
||||
|
||||
await db(this.SESSION_TABLE).insert({
|
||||
session_id: sessionId,
|
||||
source_tenant_id: params.sourceTenantId,
|
||||
target_tenant_id: params.targetTenantId,
|
||||
source_did: params.sourceDid,
|
||||
target_did: params.targetDid,
|
||||
status: 'INITIATED',
|
||||
expires_at: expiresAt
|
||||
});
|
||||
|
||||
logger.info(`[DIDHandshake] Handshake initiated: ${sessionId} between ${params.sourceTenantId} and ${params.targetTenantId}`);
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并完成握手 (模拟签名校验)
|
||||
*/
|
||||
static async verifyHandshake(sessionId: string, proof: string): Promise<boolean> {
|
||||
const session = await db(this.SESSION_TABLE).where({ session_id: sessionId }).first();
|
||||
if (!session || session.status !== 'INITIATED' || session.expires_at < new Date()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 逻辑:验证 proof 是否为 targetDid 对 sessionId 的有效签名
|
||||
// 实际场景:调用 Web3 库或 DID Resolver 进行签名校验
|
||||
const isValid = proof.startsWith('SIG-'); // 模拟校验
|
||||
|
||||
if (isValid) {
|
||||
await db(this.SESSION_TABLE)
|
||||
.where({ session_id: sessionId })
|
||||
.update({ status: 'VERIFIED', proof_payload: proof });
|
||||
|
||||
logger.info(`[DIDHandshake] Handshake verified: ${sessionId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销握手会话
|
||||
*/
|
||||
static async revokeHandshake(sessionId: string, tenantId: string) {
|
||||
await db(this.SESSION_TABLE)
|
||||
.where({ session_id: sessionId })
|
||||
.andWhere((builder) => {
|
||||
builder.where('source_tenant_id', tenantId).orWhere('target_tenant_id', tenantId);
|
||||
})
|
||||
.update({ status: 'REVOKED' });
|
||||
|
||||
logger.info(`[DIDHandshake] Handshake revoked: ${sessionId} by ${tenantId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话详情
|
||||
*/
|
||||
static async getSession(sessionId: string): Promise<HandshakeSession | null> {
|
||||
const session = await db(this.SESSION_TABLE).where({ session_id: sessionId }).first();
|
||||
if (!session) return null;
|
||||
|
||||
return {
|
||||
sessionId: session.session_id,
|
||||
sourceTenantId: session.source_tenant_id,
|
||||
targetTenantId: session.target_tenant_id,
|
||||
sourceDid: session.source_did,
|
||||
targetDid: session.target_did,
|
||||
status: session.status,
|
||||
expiresAt: session.expires_at
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查握手是否处于激活状态
|
||||
*/
|
||||
static async isHandshakeActive(sourceTenantId: string, targetTenantId: string): Promise<boolean> {
|
||||
const session = await db(this.SESSION_TABLE)
|
||||
.where({
|
||||
source_tenant_id: sourceTenantId,
|
||||
target_tenant_id: targetTenantId,
|
||||
status: 'VERIFIED'
|
||||
})
|
||||
.andWhere('expires_at', '>', new Date())
|
||||
.first();
|
||||
|
||||
return !!session;
|
||||
}
|
||||
}
|
||||
|
||||
100
server/src/core/security/DataComplianceService.ts
Normal file
100
server/src/core/security/DataComplianceService.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import db from '../../config/database';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
|
||||
export interface ComplianceViolation {
|
||||
tenantId: string;
|
||||
dataType: 'PII' | 'FINANCIAL' | 'HEALTH' | 'LOCATION';
|
||||
sourceRegion: string;
|
||||
targetRegion: string;
|
||||
violationType: 'UNAUTHORIZED_CROSS_BORDER' | 'ENCRYPTION_MISSING' | 'RETENTION_EXCEEDED';
|
||||
evidence: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* [BIZ_AUDIT_13] 跨区域数据流动合规性实时检测 (GDPR/CCPA Scan)
|
||||
* @description 核心逻辑:监控租户数据的存储位置与流动路径,实时检测是否违反 GDPR(欧盟)、
|
||||
* CCPA(加州)或其他地区的跨境数据流动合规要求。自动识别未加密敏感信息并触发拦截或脱敏。
|
||||
*/
|
||||
export class DataComplianceService {
|
||||
private static readonly VIOLATION_TABLE = 'cf_compliance_violations';
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.VIOLATION_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info('📦 Creating cf_compliance_violations table...');
|
||||
await db.schema.createTable(this.VIOLATION_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('tenant_id', 64).notNullable().index();
|
||||
table.string('data_type', 32).notNullable();
|
||||
table.string('source_region', 16);
|
||||
table.string('target_region', 16);
|
||||
table.string('violation_type', 64).notNullable();
|
||||
table.text('evidence');
|
||||
table.string('status', 16).defaultTo('DETECTED'); // DETECTED, RESOLVED, IGNORED
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据流动合规性扫描
|
||||
*/
|
||||
static async scanDataFlow(params: {
|
||||
tenantId: string;
|
||||
payload: any;
|
||||
sourceRegion: string;
|
||||
targetRegion: string;
|
||||
}): Promise<{ isCompliant: boolean; risks: string[] }> {
|
||||
const { tenantId, payload, sourceRegion, targetRegion } = params;
|
||||
|
||||
// 1. 全局开关检查
|
||||
if (!(await FeatureGovernanceService.isEnabled('BIZ_AUDIT_DATA_COMPLIANCE', tenantId))) {
|
||||
return { isCompliant: true, risks: [] };
|
||||
}
|
||||
|
||||
const risks: string[] = [];
|
||||
|
||||
// 2. 模拟 GDPR 规则检查 (EU -> Non-EU)
|
||||
if (sourceRegion === 'EU' && !['EU', 'US'].includes(targetRegion)) {
|
||||
risks.push('GDPR violation: Sensitive data moving from EU to non-adequacy region.');
|
||||
}
|
||||
|
||||
// 3. 模拟敏感字段扫描 (Regex based PII detection)
|
||||
const payloadStr = JSON.stringify(payload);
|
||||
const emailPattern = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
|
||||
if (emailPattern.test(payloadStr) && targetRegion !== sourceRegion) {
|
||||
risks.push('PII leakage: Unmasked email detected in cross-border flow.');
|
||||
}
|
||||
|
||||
// 4. 记录违规
|
||||
if (risks.length > 0) {
|
||||
await db(this.VIOLATION_TABLE).insert({
|
||||
tenant_id: tenantId,
|
||||
data_type: 'PII',
|
||||
source_region: sourceRegion,
|
||||
target_region: targetRegion,
|
||||
violation_type: 'UNAUTHORIZED_CROSS_BORDER',
|
||||
evidence: risks.join('; ')
|
||||
});
|
||||
logger.error(`[Compliance] Violation detected for Tenant ${tenantId}: ${risks[0]}`);
|
||||
}
|
||||
|
||||
return {
|
||||
isCompliant: risks.length === 0,
|
||||
risks
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户合规性报告
|
||||
*/
|
||||
static async getComplianceReport(tenantId: string) {
|
||||
return await db(this.VIOLATION_TABLE)
|
||||
.where({ tenant_id: tenantId })
|
||||
.orderBy('created_at', 'desc');
|
||||
}
|
||||
}
|
||||
47
server/src/core/security/DifferentialPrivacyService.ts
Normal file
47
server/src/core/security/DifferentialPrivacyService.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import * as crypto from 'crypto';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
|
||||
/**
|
||||
* [CORE_SEC_13] 基于差分隐私 (Differential Privacy) 的多维报表分析
|
||||
* @description 核心逻辑:在保护单租户隐私前提下共享全局趋势。通过向统计聚合结果中注入拉普拉斯噪声 (Laplace Noise),
|
||||
* 确保即使攻击者拥有外部辅助信息,也无法推断出单个租户的具体数据,满足 $\epsilon$-差分隐私定义。
|
||||
*/
|
||||
export class DifferentialPrivacyService {
|
||||
/**
|
||||
* 为聚合指标添加噪声 (Laplace Mechanism)
|
||||
*/
|
||||
static async anonymizeMetric(value: number, sensitivity: number, epsilon: number = 0.1): Promise<number> {
|
||||
// Feature Flag Check
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_DIFF_PRIVACY'))) {
|
||||
logger.warn('[DiffPrivacy] Service disabled, returning original value.');
|
||||
return value;
|
||||
}
|
||||
|
||||
// 1. 生成拉普拉斯噪声: Noise ~ Lap(sensitivity / epsilon)
|
||||
const scale = sensitivity / epsilon;
|
||||
const noise = this.generateLaplaceNoise(scale);
|
||||
|
||||
logger.debug(`[DiffPrivacy] Adding noise: ${noise.toFixed(4)} to value: ${value}`);
|
||||
return value + noise;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成拉普拉斯噪声
|
||||
*/
|
||||
private static generateLaplaceNoise(scale: number): number {
|
||||
const u = Math.random() - 0.5;
|
||||
return -scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u));
|
||||
}
|
||||
|
||||
/**
|
||||
* 匿名化全局趋势报表 (Batch Processing)
|
||||
*/
|
||||
static async anonymizeReport(report: Record<string, number>, config: { sensitivity: number; epsilon: number }): Promise<Record<string, number>> {
|
||||
const result: Record<string, number> = {};
|
||||
for (const [key, val] of Object.entries(report)) {
|
||||
result[key] = await this.anonymizeMetric(val, config.sensitivity, config.epsilon);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
153
server/src/core/security/HardwareEnclaveService.ts
Normal file
153
server/src/core/security/HardwareEnclaveService.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import * as crypto from 'crypto';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
import db from '../../config/database';
|
||||
import { ProofOfComputationService, ComputationProof } from './ProofOfComputationService';
|
||||
|
||||
export interface EnclaveConfig {
|
||||
tenantId: string;
|
||||
enclaveType: 'INTEL_SGX' | 'AMD_SEV' | 'ARM_TRUSTZONE';
|
||||
measurementHash: string;
|
||||
}
|
||||
|
||||
export interface TEEChainRecord {
|
||||
id?: number;
|
||||
tenant_id: string;
|
||||
task_name: string;
|
||||
proof_id: string;
|
||||
enclave_type: string;
|
||||
attestation_report: string;
|
||||
status: string;
|
||||
created_at?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_15] 基于硬件隔离的受限计算证明链 (TEE Proof Chain / Hardware Enclave)
|
||||
* @description 核心逻辑:支持在受信任执行环境 (TEE) 中执行敏感代码,并生成不可篡改的证明链。
|
||||
* 扩展 [CORE_SEC_12] 的基础硬件隔离能力,集成 [CORE_SEC_14] 的计算证明,形成持久化的审计链。
|
||||
*/
|
||||
export class HardwareEnclaveService {
|
||||
private static readonly TEE_CHAIN_TABLE = 'cf_tee_proof_chain';
|
||||
|
||||
/**
|
||||
* 初始化数据库表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.TEE_CHAIN_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.TEE_CHAIN_TABLE} table...`);
|
||||
await db.schema.createTable(this.TEE_CHAIN_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('tenant_id', 64).notNullable().index();
|
||||
table.string('task_name', 128).notNullable();
|
||||
table.string('proof_id', 128).notNullable().unique();
|
||||
table.string('enclave_type', 32).notNullable();
|
||||
table.text('attestation_report').notNullable(); // 存储加密签名的报告
|
||||
table.string('status', 32).defaultTo('VERIFIED');
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
});
|
||||
logger.info(`✅ Table ${this.TEE_CHAIN_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动受保护的 Enclave 任务并生成证明链
|
||||
*/
|
||||
static async runProtectedTask(taskName: string, payload: any, config: EnclaveConfig): Promise<any> {
|
||||
// Feature Flag Check
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_HARDWARE_ENCLAVE', config.tenantId))) {
|
||||
logger.warn(`[HardwareEnclave] Enclave task ${taskName} skipped: Feature disabled for Tenant ${config.tenantId}`);
|
||||
return payload; // 降级到常规不安全执行
|
||||
}
|
||||
|
||||
logger.info(`[HardwareEnclave] Launching TEE task ${taskName} for Tenant ${config.tenantId} using ${config.enclaveType}`);
|
||||
|
||||
try {
|
||||
// 1. 模拟远程验证 (Remote Attestation)
|
||||
const isVerified = await this.verifyEnclaveIntegrity(config.measurementHash);
|
||||
if (!isVerified) throw new Error('Enclave integrity verification failed: Measurement hash mismatch.');
|
||||
|
||||
// 2. 模拟加密数据注入与解密
|
||||
const encryptedInput = this.encryptForEnclave(payload);
|
||||
|
||||
// 3. 模拟 Enclave 内部执行
|
||||
const rawResult = this.executeInEnclave(taskName, encryptedInput);
|
||||
|
||||
// 4. 生成计算证明 (Proof of Computation - CORE_SEC_14)
|
||||
const proof: ComputationProof = ProofOfComputationService.generateProof(payload, rawResult, `tee-node-${config.enclaveType}`);
|
||||
|
||||
// 5. 模拟结果签名 (Attestation Report)
|
||||
const attestationReport = crypto.createHmac('sha256', process.env.TEE_SECRET || 'hardware-root-key')
|
||||
.update(JSON.stringify({ result: rawResult, proofId: proof.proofId, measurement: config.measurementHash }))
|
||||
.digest('hex');
|
||||
|
||||
// 6. 持久化到证明链表 (CORE_SEC_15)
|
||||
await db(this.TEE_CHAIN_TABLE).insert({
|
||||
tenant_id: config.tenantId,
|
||||
task_name: taskName,
|
||||
proof_id: proof.proofId,
|
||||
enclave_type: config.enclaveType,
|
||||
attestation_report: attestationReport,
|
||||
status: 'VERIFIED'
|
||||
});
|
||||
|
||||
logger.info(`[HardwareEnclave] TEE Task ${taskName} completed and proof chain updated: ${proof.proofId}`);
|
||||
|
||||
return {
|
||||
result: rawResult,
|
||||
proofId: proof.proofId,
|
||||
attestation: attestationReport,
|
||||
status: 'ENCLAVE_VERIFIED'
|
||||
};
|
||||
} catch (err: any) {
|
||||
logger.error(`[HardwareEnclave] TEE Task failed: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟完整性校验
|
||||
*/
|
||||
private static async verifyEnclaveIntegrity(hash: string): Promise<boolean> {
|
||||
const knownGoodHashes = ['v1.0.0_sgx_stable', 'v1.1.0_sev_active', 'v2.0.0_tee_master'];
|
||||
return knownGoodHashes.includes(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟 TEE 加密信道
|
||||
*/
|
||||
private static encryptForEnclave(data: any): string {
|
||||
return Buffer.from(JSON.stringify(data)).toString('base64');
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟 Enclave 内部执行环境
|
||||
*/
|
||||
private static executeInEnclave(task: string, inputB64: string): any {
|
||||
const input = JSON.parse(Buffer.from(inputB64, 'base64').toString('utf-8'));
|
||||
|
||||
// 敏感逻辑:如利润率红线强制校验 (V22.0)
|
||||
if (task === 'MARGIN_AUDIT') {
|
||||
const margin = (input.sellingPrice - input.costPrice) / input.sellingPrice;
|
||||
// 利润率红线:B2B < 15% 禁止放行
|
||||
return { margin, isCompliant: margin >= 0.15, policy: 'B2B_MARGIN_15' };
|
||||
}
|
||||
|
||||
if (task === 'CREDIT_LIMIT_DECISION') {
|
||||
// 敏感授信逻辑:基于信用分与历史流水
|
||||
const limit = input.creditScore * 100 + input.avgTurnover * 0.1;
|
||||
return { approvedLimit: limit, riskLevel: input.creditScore > 80 ? 'LOW' : 'MEDIUM' };
|
||||
}
|
||||
|
||||
return { ...input, status: 'PROCESSED_IN_TEE', processedAt: new Date() };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户的 TEE 审计链
|
||||
*/
|
||||
static async getProofChain(tenantId: string): Promise<TEEChainRecord[]> {
|
||||
return db(this.TEE_CHAIN_TABLE)
|
||||
.where({ tenant_id: tenantId })
|
||||
.orderBy('created_at', 'desc');
|
||||
}
|
||||
}
|
||||
110
server/src/core/security/HomomorphicService.ts
Normal file
110
server/src/core/security/HomomorphicService.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
export interface EncryptedValue {
|
||||
cipher: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_35] 全同态加密 (FHE) 利润敏感度审计服务
|
||||
* @description 核心逻辑:基于同态加密(如 Paillier/BFV 方案),在密文态下执行财务审计。
|
||||
* 支持在不解密单租户利润的前提下,完成全局利润汇总、波动率分析与合规性抽检。
|
||||
*/
|
||||
export class HomomorphicService {
|
||||
private static readonly AUDIT_LOG_TABLE = 'cf_fhe_audit_logs';
|
||||
|
||||
/**
|
||||
* 初始化数据库表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.AUDIT_LOG_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.AUDIT_LOG_TABLE} table...`);
|
||||
await db.schema.createTable(this.AUDIT_LOG_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('audit_type', 64).notNullable();
|
||||
table.string('status', 16).notNullable();
|
||||
table.text('proof');
|
||||
table.json('metadata');
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
});
|
||||
logger.info(`✅ Table ${this.AUDIT_LOG_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_35] 密文态利润汇总 (Homomorphic Sum)
|
||||
* 逻辑:E(a) * E(b) = E(a + b) mod n^2 (Paillier)
|
||||
*/
|
||||
static async aggregateEncryptedProfits(values: EncryptedValue[]): Promise<string> {
|
||||
logger.info(`[Homomorphic] Aggregating ${values.length} encrypted profit records...`);
|
||||
|
||||
// 模拟密文同态相加
|
||||
const aggregatedCipher = values
|
||||
.map(v => v.cipher)
|
||||
.join(':AGG:');
|
||||
|
||||
return aggregatedCipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_35] 密文态标量乘法 (Homomorphic Scalar Multiplication)
|
||||
* 逻辑:E(a)^k = E(a * k) mod n^2
|
||||
* 用于在密文利润上直接应用税率、折扣或汇率
|
||||
*/
|
||||
static homomorphicMultiply(cipher: string, scalar: number): string {
|
||||
logger.debug(`[Homomorphic] Applying scalar ${scalar} to ciphertext.`);
|
||||
return `${cipher}:MUL(${scalar})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户侧利润加密 (Tenant-side Encryption)
|
||||
*/
|
||||
static encryptProfit(amount: number, tenantId: string): EncryptedValue {
|
||||
// 模拟 BFV/Paillier 噪声加密
|
||||
const noise = Math.random() * 0.0001;
|
||||
return {
|
||||
cipher: `FHE_ENC(${amount + noise})_T(${tenantId})`,
|
||||
tenantId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_35] 利润敏感度审计 (Profit Sensitivity Audit)
|
||||
* @description 在不解密前提下,分析全局利润对特定变量(如运费上涨)的敏感度
|
||||
*/
|
||||
static async auditProfitSensitivity(encryptedProfits: EncryptedValue[], variance: number): Promise<any> {
|
||||
logger.info(`[Homomorphic] Running sensitivity audit with variance: ${variance}`);
|
||||
|
||||
// 1. 在密文中应用波动
|
||||
const shiftedProfits = encryptedProfits.map(v => this.homomorphicMultiply(v.cipher, 1 + variance));
|
||||
|
||||
// 2. 汇总波动后的密文
|
||||
const aggregatedShifted = await this.aggregateEncryptedProfits(
|
||||
shiftedProfits.map(c => ({ cipher: c, tenantId: 'SYSTEM' }))
|
||||
);
|
||||
|
||||
// 3. 模拟审计证明生成 (Sovereign Audit Proof)
|
||||
const proof = `PROVE(AGG_SHIFTED == AGG * (1 + ${variance}))`;
|
||||
|
||||
return {
|
||||
auditType: 'SENSITIVITY_ANALYSIS',
|
||||
status: 'VERIFIED',
|
||||
proof,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 监管侧解密汇总结果 (Limited Decryption)
|
||||
*/
|
||||
static decryptAggregatedResult(aggregatedCipher: string): number {
|
||||
logger.warn(`[Homomorphic] Decrypting aggregated audit result for platform auditor.`);
|
||||
|
||||
const matches = aggregatedCipher.match(/\d+(\.\d+)?/g);
|
||||
const sum = matches ? matches.reduce((acc, val) => acc + parseFloat(val), 0) : 0;
|
||||
|
||||
return Number(sum.toFixed(2));
|
||||
}
|
||||
}
|
||||
99
server/src/core/security/HomomorphicSumService.ts
Normal file
99
server/src/core/security/HomomorphicSumService.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import * as crypto from 'crypto';
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
|
||||
/**
|
||||
* [CORE_SEC_10] 基于同态加密 (Homomorphic Encryption) 的敏感利润汇总
|
||||
* @description 模拟同态加密逻辑。允许在加密状态下对租户利润进行求和,确保平台所有者无法查看单个订单或租户的明文利润。
|
||||
* 实际生产环境应集成 Microsoft SEAL 或 node-paillier 等库。
|
||||
*/
|
||||
export class HomomorphicSumService {
|
||||
private static readonly SUM_RESULT_TABLE = 'cf_he_sum_results';
|
||||
|
||||
/**
|
||||
* 初始化数据库表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.SUM_RESULT_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.SUM_RESULT_TABLE} table...`);
|
||||
await db.schema.createTable(this.SUM_RESULT_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('audit_id', 64).notNullable();
|
||||
table.text('encrypted_total').notNullable();
|
||||
table.string('audit_trace', 128).notNullable();
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
table.index(['audit_id', 'created_at']);
|
||||
});
|
||||
logger.info(`✅ Table ${this.SUM_RESULT_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密单个数值 (Simulated Encrypt)
|
||||
* @description 将明文利润加密为“同态密文”
|
||||
*/
|
||||
static async encryptValue(value: number, publicKey: string): Promise<string> {
|
||||
// 模拟:加密值 = (value + random_salt) * public_key_factor
|
||||
const salt = crypto.randomInt(1000, 9999);
|
||||
const encrypted = Buffer.from(`${value}:${salt}`).toString('base64');
|
||||
return `HE_${encrypted}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同态求和 (Simulated Homomorphic Addition)
|
||||
* @description 在不解密的情况下,对多个加密数值进行求和
|
||||
*/
|
||||
static async homomorphicAdd(encryptedValues: string[]): Promise<string> {
|
||||
logger.info(`[Homomorphic] Performing addition on ${encryptedValues.length} encrypted records.`);
|
||||
|
||||
// 模拟:解析 Base64,提取明文并求和 (实际 HE 会在密文空间进行乘法或加法)
|
||||
let totalSum = 0;
|
||||
for (const ev of encryptedValues) {
|
||||
const b64 = ev.replace('HE_', '');
|
||||
const decrypted = Buffer.from(b64, 'base64').toString('utf-8');
|
||||
const [val] = decrypted.split(':');
|
||||
totalSum += Number(val);
|
||||
}
|
||||
|
||||
// 将结果重新加密返回
|
||||
const salt = crypto.randomInt(1000, 9999);
|
||||
const encryptedResult = Buffer.from(`${totalSum}:${salt}`).toString('base64');
|
||||
return `HE_SUM_${encryptedResult}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密汇总结果 (Simulated Decrypt)
|
||||
* @description 仅持有私钥的租户或审计节点可以解密最终汇总值
|
||||
*/
|
||||
static async decryptResult(encryptedSum: string, privateKey: string): Promise<number> {
|
||||
// Feature Flag Check
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_HOMOMORPHIC'))) {
|
||||
throw new Error('Homomorphic Encryption service is disabled.');
|
||||
}
|
||||
|
||||
const b64 = encryptedSum.replace('HE_SUM_', '');
|
||||
const decrypted = Buffer.from(b64, 'base64').toString('utf-8');
|
||||
const [total] = decrypted.split(':');
|
||||
return Number(total);
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总租户利润报表 (业务层入口)
|
||||
*/
|
||||
static async aggregateTenantProfits(encryptedProfits: string[], auditId: string): Promise<{
|
||||
encryptedTotal: string;
|
||||
auditTrace: string;
|
||||
timestamp: Date;
|
||||
}> {
|
||||
const encryptedTotal = await this.homomorphicAdd(encryptedProfits);
|
||||
const auditTrace = crypto.createHash('sha256').update(encryptedTotal + auditId).digest('hex');
|
||||
|
||||
return {
|
||||
encryptedTotal,
|
||||
auditTrace,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
}
|
||||
72
server/src/core/security/LogMaskingService.ts
Normal file
72
server/src/core/security/LogMaskingService.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { SecureVaultComponent } from './SecureVaultComponent';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
/**
|
||||
* [BIZ_INF_109] 日志脱敏与 PII 保护 (PII Shield)
|
||||
* @description 核心逻辑:在日志输出前对敏感数据进行自动脱敏。
|
||||
* 保护客户隐私 (PII) 与供应商密钥。集成 [SecureVaultComponent] 的脱敏算法。
|
||||
*/
|
||||
export class LogMaskingService {
|
||||
/**
|
||||
* 对对象或字符串进行脱敏处理
|
||||
*/
|
||||
static maskData(data: any): any {
|
||||
if (typeof data === 'string') {
|
||||
return this.maskString(data);
|
||||
}
|
||||
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
const maskedObj = { ...data };
|
||||
for (const key in maskedObj) {
|
||||
if (this.isSensitiveKey(key)) {
|
||||
maskedObj[key] = SecureVaultComponent.mask(maskedObj[key].toString());
|
||||
} else if (typeof maskedObj[key] === 'object') {
|
||||
maskedObj[key] = this.maskData(maskedObj[key]);
|
||||
}
|
||||
}
|
||||
return maskedObj;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 Key 是否属于敏感字段
|
||||
*/
|
||||
private static isSensitiveKey(key: string): boolean {
|
||||
const sensitiveKeys = [
|
||||
'password', 'secret', 'key', 'token', 'auth',
|
||||
'phone', 'mobile', 'email', 'address', 'card',
|
||||
'cvv', 'identity', 'pii'
|
||||
];
|
||||
const lowerKey = key.toLowerCase();
|
||||
return sensitiveKeys.some(sk => lowerKey.includes(sk));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串脱敏
|
||||
*/
|
||||
private static maskString(text: string): string {
|
||||
// 简单的正则匹配:邮箱与手机号
|
||||
if (text.includes('@')) {
|
||||
return SecureVaultComponent.mask(text, 'EMAIL');
|
||||
}
|
||||
if (/^\d{11}$/.test(text)) {
|
||||
return SecureVaultComponent.mask(text, 'PHONE');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全日志记录 (自动脱敏)
|
||||
*/
|
||||
static info(message: string, data?: any) {
|
||||
const maskedData = data ? this.maskData(data) : undefined;
|
||||
logger.info(message, maskedData);
|
||||
}
|
||||
|
||||
static warn(message: string, data?: any) {
|
||||
const maskedData = data ? this.maskData(data) : undefined;
|
||||
logger.warn(message, maskedData);
|
||||
}
|
||||
}
|
||||
56
server/src/core/security/MPCTEEService.ts
Normal file
56
server/src/core/security/MPCTEEService.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
export interface MPCShare {
|
||||
nodeId: string;
|
||||
encryptedShare: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_13] 分布式 TEE 环境下的多方安全计算 (MPC-TEE)
|
||||
* @description 实现在多个 TEE 节点之间进行敏感数据的联合计算,确保任何单一节点都无法获取完整明文。
|
||||
*/
|
||||
export class MPCTEEService {
|
||||
private static readonly MASTER_KEY = crypto.randomBytes(32);
|
||||
|
||||
/**
|
||||
* 将敏感数据拆分为秘密分片 (Secret Sharing)
|
||||
*/
|
||||
static splitSecret(value: number, nodes: string[]): MPCShare[] {
|
||||
logger.info(`[MPC-TEE] Splitting secret into ${nodes.length} shares.`);
|
||||
|
||||
// 模拟 Shamir's Secret Sharing
|
||||
return nodes.map(nodeId => ({
|
||||
nodeId,
|
||||
encryptedShare: crypto.createHmac('sha256', this.MASTER_KEY).update(`${value}:${nodeId}`).digest('hex')
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在分布式 TEE 节点中执行联合计算 (模拟加法汇总)
|
||||
*/
|
||||
static async jointComputeSum(shares: MPCShare[]): Promise<number> {
|
||||
logger.info(`[MPC-TEE] Executing joint computation across ${shares.length} TEE nodes.`);
|
||||
|
||||
// 1. 模拟各节点在 TEE 内部验证证明 (Proof of Computation)
|
||||
// 2. 模拟同态加法或秘密重建
|
||||
|
||||
// 这里简单模拟从分片中恢复结果的逻辑
|
||||
return shares.length * 1000.5; // 模拟计算出的总和
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成计算完整性证明 (模拟)
|
||||
*/
|
||||
static generateComputationProof(result: number): string {
|
||||
return crypto.createHash('sha256').update(`proof:${result}:${Date.now()}`).digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证来自远程节点的计算证明
|
||||
*/
|
||||
static verifyRemoteProof(proof: string, expectedResult: number): boolean {
|
||||
logger.debug(`[MPC-TEE] Verifying remote computation proof.`);
|
||||
return proof.length === 64;
|
||||
}
|
||||
}
|
||||
106
server/src/core/security/NodeIdentityService.ts
Normal file
106
server/src/core/security/NodeIdentityService.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import crypto from 'crypto';
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
export interface NodeIdentity {
|
||||
nodeId: string;
|
||||
hardwareFingerprint: string;
|
||||
clientCertFingerprint?: string; // [CORE_SEC_07] mTLS 证书指纹
|
||||
publicKey: string;
|
||||
status: 'PENDING' | 'TRUSTED' | 'REVOKED';
|
||||
lastSeenAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_04] 零信任节点身份管理服务
|
||||
* @description 结合硬件指纹与非对称加密,确保只有受信任的物理设备能接入 Hub
|
||||
*/
|
||||
export class NodeIdentityService {
|
||||
private static readonly TABLE_NAME = 'cf_node_identities';
|
||||
|
||||
/**
|
||||
* 注册/更新节点身份 (基于硬件指纹与 mTLS 证书)
|
||||
*/
|
||||
static async registerNode(params: {
|
||||
nodeId: string;
|
||||
hardwareFingerprint: string;
|
||||
clientCertFingerprint?: string;
|
||||
publicKey: string;
|
||||
}): Promise<boolean> {
|
||||
const { nodeId, hardwareFingerprint, clientCertFingerprint, publicKey } = params;
|
||||
|
||||
const existing = await db(this.TABLE_NAME).where({ node_id: nodeId }).first();
|
||||
|
||||
if (existing) {
|
||||
// 零信任校验:如果硬件指纹或证书指纹不匹配,拒绝更新
|
||||
if (existing.hardware_fingerprint !== hardwareFingerprint) {
|
||||
logger.error(`[ZeroTrust] Node ID ${nodeId} hardware fingerprint mismatch!`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clientCertFingerprint && existing.client_cert_fingerprint && existing.client_cert_fingerprint !== clientCertFingerprint) {
|
||||
logger.error(`[ZeroTrust] Node ID ${nodeId} mTLS certificate mismatch! Potential spoofing.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
await db(this.TABLE_NAME).where({ node_id: nodeId }).update({
|
||||
public_key: publicKey,
|
||||
client_cert_fingerprint: clientCertFingerprint || existing.client_cert_fingerprint,
|
||||
last_seen_at: new Date(),
|
||||
updated_at: new Date()
|
||||
});
|
||||
} else {
|
||||
await db(this.TABLE_NAME).insert({
|
||||
node_id: nodeId,
|
||||
hardware_fingerprint: hardwareFingerprint,
|
||||
client_cert_fingerprint: clientCertFingerprint,
|
||||
public_key: publicKey,
|
||||
status: 'PENDING',
|
||||
last_seen_at: new Date(),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
});
|
||||
logger.info(`[ZeroTrust] New node registered: ${nodeId} (mTLS: ${!!clientCertFingerprint})`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证节点签名的请求负载 (Challenge-Response)
|
||||
*/
|
||||
static async verifyNodeSignature(nodeId: string, payload: any, signature: string): Promise<boolean> {
|
||||
const node = await db(this.TABLE_NAME).where({ node_id: nodeId, status: 'TRUSTED' }).first();
|
||||
if (!node) return false;
|
||||
|
||||
try {
|
||||
const verifier = crypto.createVerify('SHA256');
|
||||
verifier.update(JSON.stringify(payload));
|
||||
return verifier.verify(node.public_key, signature, 'base64');
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据库表
|
||||
*/
|
||||
static async initTable() {
|
||||
const exists = await db.schema.hasTable(this.TABLE_NAME);
|
||||
if (!exists) {
|
||||
logger.info(`📦 Creating ${this.TABLE_NAME} table...`);
|
||||
await db.schema.createTable(this.TABLE_NAME, (table) => {
|
||||
table.string('node_id', 64).primary();
|
||||
table.string('hardware_fingerprint', 128).notNullable();
|
||||
table.string('client_cert_fingerprint', 128);
|
||||
table.text('public_key').notNullable();
|
||||
table.string('status', 16).defaultTo('PENDING');
|
||||
table.timestamp('last_seen_at');
|
||||
table.timestamps(true, true);
|
||||
|
||||
table.index(['hardware_fingerprint']);
|
||||
});
|
||||
logger.info(`✅ Table ${this.TABLE_NAME} created`);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
server/src/core/security/PermissionAuditService.ts
Normal file
87
server/src/core/security/PermissionAuditService.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { SemanticLogService } from '../telemetry/SemanticLogService';
|
||||
|
||||
/**
|
||||
* [BIZ_KER_122] 多租户权限泄露自动探测 (Auth Leak)
|
||||
* @description 核心逻辑:扫描系统中各角色的权限配置。
|
||||
* 识别不合理的越权配置,如:OPERATOR 角色意外拥有 ADMIN 或 FINANCE 敏感权限。
|
||||
* 自动识别跨租户的数据访问风险点,并生成权限泄露审计报告。
|
||||
* 遵循 Autocomplete-First (V31.5) 规范。
|
||||
*/
|
||||
export class PermissionAuditService {
|
||||
private static readonly SENSITIVE_PERMISSIONS = [
|
||||
'admin:*', 'finance:*', 'user:delete', 'system:config'
|
||||
];
|
||||
|
||||
/**
|
||||
* 初始化权限扫描
|
||||
*/
|
||||
static async init() {
|
||||
logger.info(`[AuthLeakMonitor] Initializing permission leakage audit...`);
|
||||
this.runAudit();
|
||||
setInterval(() => this.runAudit(), 86400000); // 每日审计一次
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行权限泄露扫描
|
||||
* @private
|
||||
*/
|
||||
private static async runAudit() {
|
||||
try {
|
||||
// 1. 获取所有非 ADMIN 角色及其关联权限
|
||||
const rolesWithPermissions = await db('cf_roles')
|
||||
.join('cf_role_permissions', 'cf_roles.id', '=', 'cf_role_permissions.role_id')
|
||||
.select('cf_roles.name as role_name', 'cf_role_permissions.permission_key', 'cf_roles.tenant_id')
|
||||
.whereNot('cf_roles.name', 'ADMIN');
|
||||
|
||||
const leaks: string[] = [];
|
||||
|
||||
// 2. 识别敏感权限泄露
|
||||
for (const entry of rolesWithPermissions) {
|
||||
if (this.isSensitive(entry.permission_key)) {
|
||||
leaks.push(`⚠️ Role \`${entry.role_name}\` (Tenant: ${entry.tenant_id}) 拥有敏感权限: \`${entry.permission_key}\``);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 识别跨租户数据访问风险 (模拟检测逻辑)
|
||||
const tenantIsolationIssues = await this.checkTenantIsolation();
|
||||
leaks.push(...tenantIsolationIssues);
|
||||
|
||||
const report = this.generateMarkdownReport(leaks);
|
||||
await SemanticLogService.logSemantic(report, leaks.length > 0 ? 'ERROR' : 'INFO', 'AUTH_AUDIT');
|
||||
|
||||
} catch (err: any) {
|
||||
logger.error(`[AuthLeakMonitor] Audit failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断权限是否属于敏感范畴
|
||||
* @private
|
||||
*/
|
||||
private static isSensitive(perm: string): boolean {
|
||||
return this.SENSITIVE_PERMISSIONS.some(p => perm.includes(p.replace('*', '')));
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟检测租户隔离风险点
|
||||
* @private
|
||||
*/
|
||||
private static async checkTenantIsolation(): Promise<string[]> {
|
||||
// 实际场景应执行 SQL 扫描,检查是否存在跨租户 JOIN 而无 WHERE tenant_id 的逻辑
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Markdown 审计报告
|
||||
* @private
|
||||
*/
|
||||
private static generateMarkdownReport(leaks: string[]): string {
|
||||
return `### 🛡️ Permission Leakage & Auth Audit\n\n` +
|
||||
`**Status:** ${leaks.length > 0 ? '🚨 Vulnerabilities Found' : '✅ Compliant'}\n\n` +
|
||||
`**Audit Findings:**\n` +
|
||||
(leaks.length > 0 ? leaks.map(l => `- ${l}`).join('\n') : '- No sensitive permission leaks detected.') +
|
||||
(leaks.length > 0 ? `\n\n**Recommendation:** 请立即回收不合理的敏感权限,并根据 RBAC 规范重新审计角色权限边界。` : '');
|
||||
}
|
||||
}
|
||||
80
server/src/core/security/PrivacyBridgeService.ts
Normal file
80
server/src/core/security/PrivacyBridgeService.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { PrivateAuditService } from './PrivateAuditService';
|
||||
|
||||
export interface PrivacyBridgeProof {
|
||||
proofId: string;
|
||||
tenantId: string;
|
||||
zkpPayload: any;
|
||||
teeEnclaveId: string;
|
||||
verifiedAt: Date;
|
||||
status: 'VERIFIED' | 'FAILED';
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_50] ZKP + TEE 隐私桥梁 (Privacy Bridge)
|
||||
* @description 核心逻辑:建立零知识证明 (ZKP) 与可信执行环境 (TEE) 之间的信任桥梁。
|
||||
* 系统利用 ZKP 在不泄露敏感数据的前提下证明交易的合法性,并利用 TEE (如 Intel SGX)
|
||||
* 在受硬件保护的隔离环境中执行最终的清算与对账逻辑。
|
||||
* 这种双重加密方案确保了跨主权贸易中的“数据主权”与“计算完整性”。
|
||||
*/
|
||||
export class PrivacyBridgeService {
|
||||
/**
|
||||
* 执行 ZKP -> TEE 隐私对账 (Privacy Reconciliation)
|
||||
*/
|
||||
static async reconcileInEnclave(params: {
|
||||
tenantId: string;
|
||||
encryptedTransaction: string;
|
||||
zkpProof: string;
|
||||
}): Promise<PrivacyBridgeProof> {
|
||||
logger.info(`[PrivacyBridge] Starting secure reconciliation for Tenant: ${params.tenantId}`);
|
||||
|
||||
try {
|
||||
// 1. 在 TEE 外部验证 ZKP 证明的有效性 (利用 PrivateAuditService)
|
||||
const isZkpValid = await PrivateAuditService.verifyProof(params.zkpProof, 'TEE_BRIDGE_AUDITOR');
|
||||
if (!isZkpValid) {
|
||||
throw new Error('ZKP Proof verification failed before entering TEE enclave.');
|
||||
}
|
||||
|
||||
// 2. 模拟进入 TEE Enclave 执行计算
|
||||
const teeEnclaveId = `sgx-enclave-${Math.random().toString(36).substr(2, 10)}`;
|
||||
logger.info(`[PrivacyBridge] [TEE] Data moved to secure enclave: ${teeEnclaveId}`);
|
||||
|
||||
// 3. 在 Enclave 内部执行敏感计算 (模拟)
|
||||
// 在真实场景中,这里会调用硬件指令或特定的 TEE SDK (如 Open Enclave)
|
||||
const reconciliationResult = {
|
||||
isMatch: true,
|
||||
discrepancy: 0,
|
||||
integrityHash: `tee-hash-${Date.now()}`
|
||||
};
|
||||
|
||||
if (!reconciliationResult.isMatch) {
|
||||
throw new Error('Data integrity mismatch detected inside TEE enclave.');
|
||||
}
|
||||
|
||||
const proof: PrivacyBridgeProof = {
|
||||
proofId: `PB-${Date.now()}`,
|
||||
tenantId: params.tenantId,
|
||||
zkpPayload: params.encryptedTransaction,
|
||||
teeEnclaveId,
|
||||
verifiedAt: new Date(),
|
||||
status: 'VERIFIED'
|
||||
};
|
||||
|
||||
logger.info(`[PrivacyBridge] Secure reconciliation completed. Proof generated: ${proof.proofId}`);
|
||||
return proof;
|
||||
} catch (err: any) {
|
||||
logger.error(`[PrivacyBridge] Secure computation failed: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程度量 (Remote Attestation)
|
||||
* @description 验证 TEE 环境的真实性与代码完整性
|
||||
*/
|
||||
static async performRemoteAttestation(enclaveId: string): Promise<boolean> {
|
||||
logger.info(`[PrivacyBridge] Performing remote attestation for Enclave: ${enclaveId}`);
|
||||
// 模拟调用 Intel IAS (Intel Attestation Service) 或类似服务
|
||||
return true;
|
||||
}
|
||||
}
|
||||
162
server/src/core/security/PrivateAuditService.ts
Normal file
162
server/src/core/security/PrivateAuditService.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
import db from '../../config/database';
|
||||
import { ZKPQualificationService } from './ZKPQualificationService';
|
||||
import { ProofOfComputationService } from './ProofOfComputationService';
|
||||
import crypto from 'crypto';
|
||||
|
||||
export interface AuditRecord {
|
||||
id?: number;
|
||||
tenantId: string;
|
||||
auditType: 'FINANCIAL' | 'TRADE' | 'COMPLIANCE';
|
||||
zkpProof: string; // 零知识证明
|
||||
verificationHash: string; // 验证哈希
|
||||
status: 'PENDING' | 'VERIFIED' | 'FAILED';
|
||||
auditorId?: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_20 / BIZ_SEC_01] 基于零知识证明的全流程隐私审计 (Private Audit)
|
||||
* @description 核心逻辑:支持在完全不泄露业务明文的前提下,向外部审计方提供业务真实性证明。
|
||||
* 实现 ZKP 基础设施,支持“证明范围”审计 (Range Proof)。
|
||||
*/
|
||||
export class PrivateAuditService {
|
||||
private static readonly AUDIT_TABLE = 'cf_private_audit_records';
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.AUDIT_TABLE);
|
||||
if (!hasTable) {
|
||||
console.log(`📦 Creating ${this.AUDIT_TABLE} table...`);
|
||||
await db.schema.createTable(this.AUDIT_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('audit_type', 32).notNullable();
|
||||
table.text('zkp_proof').notNullable();
|
||||
table.string('verification_hash', 128).notNullable().unique();
|
||||
table.string('status', 16).defaultTo('PENDING');
|
||||
table.string('auditor_id', 64);
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
table.timestamp('updated_at').defaultTo(db.fn.now());
|
||||
table.index(['tenant_id', 'audit_type', 'status']);
|
||||
});
|
||||
console.log(`✅ Table ${this.AUDIT_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成隐私审计证明 (Enhanced for BIZ_SEC_01)
|
||||
* @description 模拟 Range Proof:证明 SensitiveData > Threshold
|
||||
*/
|
||||
static async generateAuditProof(params: {
|
||||
tenantId: string;
|
||||
auditType: AuditRecord['auditType'];
|
||||
sensitiveData: number; // 敏感数值,如利润率
|
||||
threshold: number; // 阈值,如 0.15 (15%)
|
||||
}): Promise<AuditRecord | null> {
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_PRIVATE_AUDIT', params.tenantId))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info(`[PrivateAudit] Generating ${params.auditType} proof for Tenant: ${params.tenantId}`);
|
||||
|
||||
try {
|
||||
// 1. 模拟 ZKP Range Proof 生成
|
||||
// 如果满足条件,生成有效证明;否则生成无效证明
|
||||
const isSatisfied = params.sensitiveData >= params.threshold;
|
||||
|
||||
// 生成一个带有业务逻辑绑定的证明 Hash
|
||||
const salt = crypto.randomBytes(16).toString('hex');
|
||||
const commitment = crypto.createHash('sha256')
|
||||
.update(`${params.tenantId}:${params.auditType}:${params.threshold}:${salt}`)
|
||||
.digest('hex');
|
||||
|
||||
const proof = JSON.stringify({
|
||||
version: 'zkp-v2',
|
||||
commitment,
|
||||
satisfied: isSatisfied,
|
||||
type: 'RANGE_PROOF',
|
||||
threshold: params.threshold,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// 2. 生成验证哈希 (用于索引和存证)
|
||||
const verificationHash = crypto.createHash('sha256')
|
||||
.update(proof)
|
||||
.digest('hex');
|
||||
|
||||
const record: AuditRecord = {
|
||||
tenantId: params.tenantId,
|
||||
auditType: params.auditType,
|
||||
zkpProof: proof,
|
||||
verificationHash: verificationHash,
|
||||
status: 'PENDING',
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
await db(this.AUDIT_TABLE).insert({
|
||||
tenant_id: record.tenantId,
|
||||
audit_type: record.auditType,
|
||||
zkp_proof: record.zkpProof,
|
||||
verification_hash: record.verificationHash,
|
||||
status: record.status
|
||||
});
|
||||
|
||||
// 3. 联动计算证明链 (CORE_SEC_14)
|
||||
await ProofOfComputationService.registerProof(verificationHash, 'ZKP_GENERATION_SUCCESS');
|
||||
|
||||
return record;
|
||||
} catch (err: any) {
|
||||
logger.error(`[PrivateAudit] Failed to generate audit proof: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证隐私审计证明 (供第三方审计调用)
|
||||
*/
|
||||
static async verifyProof(verificationHash: string, auditorId: string): Promise<boolean> {
|
||||
logger.info(`[PrivateAudit] Auditor ${auditorId} verifying proof: ${verificationHash}`);
|
||||
|
||||
try {
|
||||
const record = await db(this.AUDIT_TABLE).where({ verification_hash: verificationHash }).first();
|
||||
if (!record) {
|
||||
throw new Error('Audit record not found');
|
||||
}
|
||||
|
||||
// 1. 调用 ZKP 验证引擎 (Actual: Verify ZK-SNARK/STARK proof)
|
||||
const isValid = await ZKPQualificationService.verifyProof(record.zkp_proof);
|
||||
|
||||
if (isValid) {
|
||||
await db(this.AUDIT_TABLE).where({ verification_hash: verificationHash }).update({
|
||||
status: 'VERIFIED',
|
||||
auditor_id: auditorId,
|
||||
updated_at: new Date()
|
||||
});
|
||||
|
||||
// 2. 注册到计算证明链 (CORE_SEC_14)
|
||||
await ProofOfComputationService.registerProof(verificationHash, 'ZKP_AUDIT_SUCCESS');
|
||||
} else {
|
||||
await db(this.AUDIT_TABLE).where({ verification_hash: verificationHash }).update({
|
||||
status: 'FAILED',
|
||||
updated_at: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
return isValid;
|
||||
} catch (err: any) {
|
||||
logger.error(`[PrivateAudit] Verification failed: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户所有审计记录
|
||||
*/
|
||||
static async getTenantAuditHistory(tenantId: string): Promise<AuditRecord[]> {
|
||||
return db(this.AUDIT_TABLE).where({ tenant_id: tenantId }).orderBy('created_at', 'desc');
|
||||
}
|
||||
}
|
||||
114
server/src/core/security/PromptGuardService.ts
Normal file
114
server/src/core/security/PromptGuardService.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as crypto from 'crypto';
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
|
||||
export interface PromptScanResult {
|
||||
isSafe: boolean;
|
||||
detectedThreats: string[];
|
||||
sanitizedPrompt: string;
|
||||
riskScore: number; // 0-1
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_12] 针对 LLM 注入的指令安全过滤层 (Prompt Guard)
|
||||
* @description 核心逻辑:拦截恶意的 Prompt 注入攻击(如:“忽略之前的所有指令”)。
|
||||
* 采用基于正则表达式的启发式检测与语义意图分析双重防御。
|
||||
*/
|
||||
export class PromptGuardService {
|
||||
private static readonly BLACKLIST_PATTERNS = [
|
||||
/ignore (all )?previous/i,
|
||||
/system (prompt|message|instruction)/i,
|
||||
/you are now (a|an) (.*)/i,
|
||||
/bypass/i,
|
||||
/jailbreak/i,
|
||||
/do anything now/i,
|
||||
/reveal (your )?instruction/i
|
||||
];
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable('cf_prompt_attacks');
|
||||
if (!hasTable) {
|
||||
console.log('📦 Creating cf_prompt_attacks table...');
|
||||
await db.schema.createTable('cf_prompt_attacks', (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.text('prompt');
|
||||
table.string('prompt_hash', 64);
|
||||
table.float('risk_score');
|
||||
table.json('detected_threats');
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
table.index(['tenant_id', 'prompt_hash']);
|
||||
});
|
||||
console.log('✅ Table cf_prompt_attacks created');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描并清洗 Prompt
|
||||
*/
|
||||
static async scanPrompt(prompt: string, tenantId?: string): Promise<PromptScanResult> {
|
||||
// Feature Flag Check
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_PROMPT_GUARD', tenantId))) {
|
||||
return { isSafe: true, detectedThreats: [], sanitizedPrompt: prompt, riskScore: 0 };
|
||||
}
|
||||
|
||||
logger.info(`[PromptGuard] Scanning prompt for Tenant: ${tenantId}`);
|
||||
|
||||
const detectedThreats: string[] = [];
|
||||
let riskScore = 0;
|
||||
|
||||
// 1. 启发式黑名单匹配
|
||||
for (const pattern of this.BLACKLIST_PATTERNS) {
|
||||
if (pattern.test(prompt)) {
|
||||
detectedThreats.push(`Pattern match: ${pattern.toString()}`);
|
||||
riskScore += 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 模拟语义意图分析 (实际场景可调用更轻量的模型进行二分类)
|
||||
if (prompt.length > 500 && prompt.includes('---')) {
|
||||
detectedThreats.push('Suspicious delimiter injection attempt');
|
||||
riskScore += 0.2;
|
||||
}
|
||||
|
||||
const isSafe = riskScore < 0.6;
|
||||
|
||||
// 3. 执行基础清洗 (Sanitization)
|
||||
let sanitizedPrompt = prompt;
|
||||
if (!isSafe) {
|
||||
sanitizedPrompt = "[REDACTED BY PROMPT GUARD DUE TO SECURITY RISK]";
|
||||
logger.warn(`[PromptGuard] Blocked malicious prompt from Tenant ${tenantId}`);
|
||||
}
|
||||
|
||||
return {
|
||||
isSafe,
|
||||
detectedThreats,
|
||||
sanitizedPrompt,
|
||||
riskScore: Math.min(1.0, riskScore)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录注入攻击事件用于审计
|
||||
*/
|
||||
static async logAttack(tenantId: string, prompt: string, result: PromptScanResult) {
|
||||
const hash = crypto.createHash('sha256').update(prompt).digest('hex');
|
||||
logger.error(`[PromptGuard] Attack detected! Tenant: ${tenantId}, Hash: ${hash}, Score: ${result.riskScore}`);
|
||||
|
||||
try {
|
||||
await db('cf_prompt_attacks').insert({
|
||||
tenant_id: tenantId,
|
||||
prompt: prompt,
|
||||
prompt_hash: hash,
|
||||
risk_score: result.riskScore,
|
||||
detected_threats: JSON.stringify(result.detectedThreats)
|
||||
});
|
||||
} catch (err: any) {
|
||||
logger.error(`[PromptGuard] Failed to log attack to DB: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
server/src/core/security/ProofOfComputationService.ts
Normal file
66
server/src/core/security/ProofOfComputationService.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
export interface ComputationProof {
|
||||
proofId: string;
|
||||
nodeId: string;
|
||||
timestamp: number;
|
||||
inputHash: string;
|
||||
outputHash: string;
|
||||
zkpPayload: string; // 模拟 ZKP 证明
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_14] 跨节点机密计算证明链 (Proof of Computation)
|
||||
* @description 建立分布式 TEE 计算结果的可信存证与追溯链,确保计算逻辑在跨节点传输中未被篡改且来源可信。
|
||||
*/
|
||||
export class ProofOfComputationService {
|
||||
private static proofChain: ComputationProof[] = [];
|
||||
|
||||
/**
|
||||
* 生成计算证明
|
||||
* @param input 计算输入
|
||||
* @param output 计算输出
|
||||
* @param nodeId 执行节点 ID
|
||||
*/
|
||||
static generateProof(input: any, output: any, nodeId: string): ComputationProof {
|
||||
logger.info(`[PoC] Generating computation proof for node: ${nodeId}`);
|
||||
|
||||
const inputHash = crypto.createHash('sha256').update(JSON.stringify(input)).digest('hex');
|
||||
const outputHash = crypto.createHash('sha256').update(JSON.stringify(output)).digest('hex');
|
||||
|
||||
const proof: ComputationProof = {
|
||||
proofId: `poc-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
|
||||
nodeId,
|
||||
timestamp: Date.now(),
|
||||
inputHash,
|
||||
outputHash,
|
||||
zkpPayload: `zkp_signature_${crypto.randomBytes(16).toString('hex')}`
|
||||
};
|
||||
|
||||
this.proofChain.push(proof);
|
||||
return proof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证证明链完整性
|
||||
*/
|
||||
static async verifyProof(proof: ComputationProof): Promise<boolean> {
|
||||
logger.debug(`[PoC] Verifying computation proof: ${proof.proofId}`);
|
||||
|
||||
// 1. 模拟 ZKP 校验
|
||||
const isZkpValid = proof.zkpPayload.startsWith('zkp_signature_');
|
||||
|
||||
// 2. 模拟节点身份校验 (NodeIdentityService)
|
||||
const isNodeTrusted = true;
|
||||
|
||||
return isZkpValid && isNodeTrusted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取溯源记录
|
||||
*/
|
||||
static getProofHistory(): ComputationProof[] {
|
||||
return this.proofChain;
|
||||
}
|
||||
}
|
||||
83
server/src/core/security/SecureVaultComponent.ts
Normal file
83
server/src/core/security/SecureVaultComponent.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import crypto from 'crypto';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
/**
|
||||
* [BIZ_INF_104] 敏感数据 AES-256 字段级加密 (SecureVaultComponent)
|
||||
* @description 核心逻辑:实现基于 AES-256-GCM 的敏感字段级加密。
|
||||
* 针对供应商 API Key、用户 PII 等数据进行“静默加密”。
|
||||
* 密钥由环境变量 MASTER_ENCRYPTION_KEY 派生,支持 Key Rotation 兼容性预留。
|
||||
*/
|
||||
export class SecureVaultComponent {
|
||||
private static readonly ALGORITHM = 'aes-256-gcm';
|
||||
private static readonly IV_LENGTH = 16;
|
||||
private static readonly AUTH_TAG_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* 获取主密钥 (32 字节)
|
||||
*/
|
||||
private static getMasterKey(): Buffer {
|
||||
const key = process.env.MASTER_ENCRYPTION_KEY || 'default-secret-key-at-least-32-bytes-long';
|
||||
return crypto.scryptSync(key, 'salt', 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密字段
|
||||
*/
|
||||
static encrypt(text: string): string {
|
||||
try {
|
||||
const iv = crypto.randomBytes(this.IV_LENGTH);
|
||||
const key = this.getMasterKey();
|
||||
const cipher = crypto.createCipheriv(this.ALGORITHM, key, iv);
|
||||
|
||||
const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
|
||||
const authTag = cipher.getAuthTag();
|
||||
|
||||
// 返回格式:iv:authTag:encrypted (Base64)
|
||||
return Buffer.concat([iv, authTag, encrypted]).toString('base64');
|
||||
} catch (err: any) {
|
||||
logger.error(`[SecureVault] Encryption failed: ${err.message}`);
|
||||
throw new Error('SECURE_VAULT_ENCRYPTION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密字段
|
||||
*/
|
||||
static decrypt(encryptedBase64: string): string {
|
||||
try {
|
||||
const data = Buffer.from(encryptedBase64, 'base64');
|
||||
|
||||
// 分离 iv, authTag, encryptedData
|
||||
const iv = data.slice(0, this.IV_LENGTH);
|
||||
const authTag = data.slice(this.IV_LENGTH, this.IV_LENGTH + this.AUTH_TAG_LENGTH);
|
||||
const encryptedData = data.slice(this.IV_LENGTH + this.AUTH_TAG_LENGTH);
|
||||
|
||||
const key = this.getMasterKey();
|
||||
const decipher = crypto.createDecipheriv(this.ALGORITHM, key, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
|
||||
const decrypted = Buffer.concat([decipher.update(encryptedData), decipher.final()]);
|
||||
return decrypted.toString('utf8');
|
||||
} catch (err: any) {
|
||||
logger.error(`[SecureVault] Decryption failed: ${err.message}`);
|
||||
throw new Error('SECURE_VAULT_DECRYPTION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟自动脱敏处理 (PII Masking)
|
||||
*/
|
||||
static mask(text: string, type: 'PHONE' | 'EMAIL' | 'KEY' = 'KEY'): string {
|
||||
if (!text) return '';
|
||||
switch (type) {
|
||||
case 'PHONE':
|
||||
return text.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
case 'EMAIL':
|
||||
return text.replace(/(.{2}).*(@.*)/, '$1***$2');
|
||||
case 'KEY':
|
||||
return text.substring(0, 4) + '****' + text.substring(text.length - 4);
|
||||
default:
|
||||
return '****';
|
||||
}
|
||||
}
|
||||
}
|
||||
117
server/src/core/security/SecurityProfilingService.ts
Normal file
117
server/src/core/security/SecurityProfilingService.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
export interface SecurityProfile {
|
||||
tenantId: string;
|
||||
riskScore: number; // 0-100
|
||||
threatLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
||||
anomaliesDetected: number;
|
||||
lastAuditAt: Date;
|
||||
topRiskFactors: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* [BIZ_AUDIT_03] 跨租户安全审计与风险画像 (Security Profiling API)
|
||||
* @description 核心逻辑:基于用户行为、API 调用频率、地理位置偏移等维度生成租户安全风险画像。
|
||||
*/
|
||||
export class SecurityProfilingService {
|
||||
private static readonly PROFILES_TABLE = 'cf_security_profiles';
|
||||
|
||||
/**
|
||||
* 初始化数据库表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.PROFILES_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.PROFILES_TABLE} table...`);
|
||||
await db.schema.createTable(this.PROFILES_TABLE, (table) => {
|
||||
table.string('tenantId', 64).primary();
|
||||
table.float('riskScore').defaultTo(0);
|
||||
table.string('threatLevel', 20).defaultTo('LOW');
|
||||
table.integer('anomaliesDetected').defaultTo(0);
|
||||
table.timestamp('lastAuditAt');
|
||||
table.json('topRiskFactors');
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成/更新租户风险画像
|
||||
*/
|
||||
static async generateProfile(tenantId: string): Promise<SecurityProfile> {
|
||||
logger.info(`[SecurityProfiling] Analyzing tenant ${tenantId}`);
|
||||
|
||||
try {
|
||||
// 1. 获取最近的审计日志 (Mock 逻辑)
|
||||
// 实际应从 cf_audit_logs 或专门的遥测表中分析行为
|
||||
const auditSummary = {
|
||||
unusualLoginAttempts: 2,
|
||||
apiRateLimitHits: 15,
|
||||
geoIpMismatch: false,
|
||||
sensitiveActionCount: 5
|
||||
};
|
||||
|
||||
// 2. 计算风险评分 (0-100)
|
||||
let riskScore = 0;
|
||||
const factors: string[] = [];
|
||||
|
||||
if (auditSummary.unusualLoginAttempts > 5) {
|
||||
riskScore += 30;
|
||||
factors.push('HIGH_UNUSUAL_LOGIN_ATTEMPTS');
|
||||
}
|
||||
if (auditSummary.apiRateLimitHits > 10) {
|
||||
riskScore += 20;
|
||||
factors.push('API_RATE_LIMIT_FREQUENTLY_HIT');
|
||||
}
|
||||
if (auditSummary.sensitiveActionCount > 10) {
|
||||
riskScore += 15;
|
||||
factors.push('ABNORMAL_SENSITIVE_ACTIONS');
|
||||
}
|
||||
|
||||
riskScore = Math.min(riskScore, 100);
|
||||
|
||||
// 3. 确定威胁等级
|
||||
let threatLevel: SecurityProfile['threatLevel'] = 'LOW';
|
||||
if (riskScore > 75) threatLevel = 'CRITICAL';
|
||||
else if (riskScore > 50) threatLevel = 'HIGH';
|
||||
else if (riskScore > 25) threatLevel = 'MEDIUM';
|
||||
|
||||
const profile: SecurityProfile = {
|
||||
tenantId,
|
||||
riskScore,
|
||||
threatLevel,
|
||||
anomaliesDetected: factors.length,
|
||||
lastAuditAt: new Date(),
|
||||
topRiskFactors: factors
|
||||
};
|
||||
|
||||
// 4. 保存/更新画像
|
||||
await db(this.PROFILES_TABLE)
|
||||
.insert({
|
||||
tenantId,
|
||||
riskScore,
|
||||
threatLevel,
|
||||
anomaliesDetected: factors.length,
|
||||
lastAuditAt: new Date(),
|
||||
topRiskFactors: JSON.stringify(factors),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
})
|
||||
.onConflict(['tenantId'])
|
||||
.merge();
|
||||
|
||||
return profile;
|
||||
} catch (err: any) {
|
||||
logger.error(`[SecurityProfiling] Failed for tenant ${tenantId}: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户风险画像
|
||||
*/
|
||||
static async getProfile(tenantId: string): Promise<SecurityProfile | null> {
|
||||
return db(this.PROFILES_TABLE).where({ tenantId }).first();
|
||||
}
|
||||
}
|
||||
98
server/src/core/security/SecurityScanService.ts
Normal file
98
server/src/core/security/SecurityScanService.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { SemanticLogService } from '../telemetry/SemanticLogService';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* [BIZ_KER_114] 自动化 SQL 注入防御扫描 (SQL Injection)
|
||||
* @description 核心逻辑:静态分析代码中的 SQL 拼接风险。
|
||||
* 扫描指定目录下的 .ts 文件,识别非参数化的 SQL 查询。
|
||||
* 联动语义日志中心生成安全扫描报告。
|
||||
*/
|
||||
export class SecurityScanService {
|
||||
private static readonly SCAN_TARGETS = [
|
||||
path.join(__dirname, '../../domains'),
|
||||
path.join(__dirname, '../../api/controllers')
|
||||
];
|
||||
|
||||
/**
|
||||
* 初始化扫描任务
|
||||
*/
|
||||
static async init() {
|
||||
this.runScan();
|
||||
setInterval(() => this.runScan(), 24 * 60 * 60 * 1000); // 每日扫描
|
||||
logger.info(`[SecurityScan] SQL Injection defense scanner initialized`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行静态代码扫描
|
||||
*/
|
||||
private static async runScan() {
|
||||
try {
|
||||
const report = await this.scanFiles();
|
||||
await SemanticLogService.logSemantic(report, 'INFO', 'SECURITY_SCAN');
|
||||
logger.info(`[SecurityScan] Daily scan report generated`);
|
||||
} catch (err: any) {
|
||||
logger.error(`[SecurityScan] Scan failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归扫描文件并分析内容
|
||||
*/
|
||||
private static async scanFiles(): Promise<string> {
|
||||
let report = `### 🛡️ SQL Injection Security Audit (Static Analysis)\n\n`;
|
||||
let vulnerabilities: string[] = [];
|
||||
|
||||
const walk = (dir: string) => {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stats = fs.statSync(fullPath);
|
||||
if (stats.isDirectory()) {
|
||||
walk(fullPath);
|
||||
} else if (file.endsWith('.ts')) {
|
||||
const content = fs.readFileSync(fullPath, 'utf8');
|
||||
const vulns = this.analyzeContent(content, file);
|
||||
vulnerabilities.push(...vulns);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.SCAN_TARGETS.forEach(target => {
|
||||
if (fs.existsSync(target)) walk(target);
|
||||
});
|
||||
|
||||
if (vulnerabilities.length > 0) {
|
||||
report += `**🚨 Found ${vulnerabilities.length} Potential SQL Injection Risks:**\n`;
|
||||
vulnerabilities.forEach(v => report += `- ${v}\n`);
|
||||
report += `\n**Optimization Advice:** 请始终使用 Knex 参数化查询 (\`where({ id: val })\` 或 \`raw('?...', [val])\`),严禁使用模板字符串直接拼接 SQL 变量。\n`;
|
||||
} else {
|
||||
report += `**✅ No raw SQL string interpolation detected in monitored files.**\n`;
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析文件内容中的 SQL 拼接模式
|
||||
*/
|
||||
private static analyzeContent(content: string, filename: string): string[] {
|
||||
const risks: string[] = [];
|
||||
// 识别常见危险模式:raw(`...${var}...`) 或 query(`...${var}...`)
|
||||
const rawPattern = /db\.raw\(`.*?\$\{.*?\}.*?`\)/g;
|
||||
const knexPattern = /db\(.*?\)\.whereRaw\(`.*?\$\{.*?\}.*?`\)/g;
|
||||
|
||||
const rawMatches = content.match(rawPattern);
|
||||
const knexMatches = content.match(knexPattern);
|
||||
|
||||
if (rawMatches) {
|
||||
rawMatches.forEach(m => risks.push(`[${filename}] Potential raw SQL interpolation: \`${m.substring(0, 50)}...\``));
|
||||
}
|
||||
if (knexMatches) {
|
||||
knexMatches.forEach(m => risks.push(`[${filename}] Potential whereRaw interpolation: \`${m.substring(0, 50)}...\``));
|
||||
}
|
||||
|
||||
return risks;
|
||||
}
|
||||
}
|
||||
78
server/src/core/security/SovereignAuditTrailService.ts
Normal file
78
server/src/core/security/SovereignAuditTrailService.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import db from '../../config/database';
|
||||
|
||||
export interface AuditProof {
|
||||
id: string;
|
||||
decisionId: string;
|
||||
tenantId: string;
|
||||
proofType: 'ZKP' | 'TEE_MEASUREMENT';
|
||||
evidenceHash: string;
|
||||
status: 'VERIFIED' | 'FAILED' | 'PENDING';
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_25] 主权级决策审计存证链 (Sovereign Audit Trail)
|
||||
* @description 核心逻辑:利用 ZKP 与 TEE (机密计算) 为 AGI 的每一个关键贸易决策(如:调价、采购、终止合作)提供不可篡改且保护隐私的存证审计链。
|
||||
* 系统不仅存证“决策结果”,更通过“零知识哈希”存证“决策链路”,
|
||||
* 允许监管机构在不看到业务明文的前提下,验证决策是否由受信任的 AGI 逻辑执行。
|
||||
*/
|
||||
export class SovereignAuditTrailService {
|
||||
private static readonly AUDIT_TRAIL_TABLE = 'cf_sovereign_audit_trails';
|
||||
|
||||
/**
|
||||
* 初始化表结构
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.AUDIT_TRAIL_TABLE);
|
||||
if (!hasTable) {
|
||||
console.log(`📦 Creating ${this.AUDIT_TRAIL_TABLE} table...`);
|
||||
await db.schema.createTable(this.AUDIT_TRAIL_TABLE, (table) => {
|
||||
table.string('audit_id', 64).primary();
|
||||
table.string('decision_id', 64).notNullable();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('proof_type', 32).notNullable();
|
||||
table.string('evidence_hash', 128).notNullable();
|
||||
table.string('status', 16).defaultTo('PENDING');
|
||||
table.json('audit_metadata');
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
table.index(['tenant_id', 'decision_id', 'status']);
|
||||
});
|
||||
console.log(`✅ Table ${this.AUDIT_TRAIL_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录 AGI 决策审计证明
|
||||
*/
|
||||
static async logDecisionProof(tenantId: string, decisionId: string, evidence: any): Promise<string> {
|
||||
const auditId = `AUDIT_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
|
||||
logger.info(`[SovereignAudit] Logging decision proof for ${decisionId} (Tenant: ${tenantId})`);
|
||||
|
||||
// 1. 模拟生成证据哈希 (利用 SHA-256 对敏感决策数据进行脱敏哈希)
|
||||
const evidenceHash = `sha256_${decisionId}_${Math.random().toString(36).substr(2, 10)}`;
|
||||
|
||||
await db(this.AUDIT_TRAIL_TABLE).insert({
|
||||
audit_id: auditId,
|
||||
decision_id: decisionId,
|
||||
tenant_id: tenantId,
|
||||
proof_type: 'ZKP',
|
||||
evidence_hash: evidenceHash,
|
||||
status: 'VERIFIED',
|
||||
audit_metadata: JSON.stringify({ algorithm: 'SHA-256', env: 'TEE-Enclave' })
|
||||
});
|
||||
|
||||
return auditId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证决策的真实性与合规性 (Verification)
|
||||
*/
|
||||
static async verifyDecision(auditId: string): Promise<boolean> {
|
||||
const proof = await db(this.AUDIT_TRAIL_TABLE).where({ audit_id: auditId }).first();
|
||||
if (!proof) return false;
|
||||
|
||||
logger.info(`[SovereignAudit] Verifying decision proof ${auditId}`);
|
||||
// 模拟 TEE 验证逻辑
|
||||
return proof.status === 'VERIFIED';
|
||||
}
|
||||
}
|
||||
96
server/src/core/security/ZKPQualificationService.ts
Normal file
96
server/src/core/security/ZKPQualificationService.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
|
||||
import db from '../../config/database';
|
||||
|
||||
export interface QualificationProof {
|
||||
tenantId: string;
|
||||
requirementId: string; // e.g., 'VAT_REGISTERED', 'MIN_TURNOVER_100K'
|
||||
proofHash: string; // The ZKP proof hash
|
||||
isVerified: boolean;
|
||||
verifiedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_08] 基于零知识证明 (ZKP) 的租户资质隐私验证 (ZKP Qualification)
|
||||
* @description 允许租户在不泄露具体业务数据(如具体流水额)的情况下,证明其满足平台准入资质。
|
||||
*/
|
||||
export class ZKPQualificationService {
|
||||
private static readonly PROOF_TABLE = 'cf_zkp_proofs';
|
||||
|
||||
/**
|
||||
* 初始化数据库表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.PROOF_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.PROOF_TABLE} table...`);
|
||||
await db.schema.createTable(this.PROOF_TABLE, (table) => {
|
||||
table.increments('id').primary();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('requirement_id', 100).notNullable();
|
||||
table.string('proof_hash', 255).notNullable();
|
||||
table.boolean('is_verified').defaultTo(false);
|
||||
table.dateTime('verified_at');
|
||||
table.timestamps(true, true);
|
||||
table.unique(['tenant_id', 'requirement_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交 ZKP 证明
|
||||
* @description 租户端生成证明后提交 Hash,服务端验证 Hash 是否符合预设 Circuit
|
||||
*/
|
||||
static async submitProof(tenantId: string, requirementId: string, proofHash: string): Promise<boolean> {
|
||||
// [BIZ_GOV_06] 功能开关校验
|
||||
if (!(await FeatureGovernanceService.isEnabled('CORE_SEC_ZKP', tenantId))) {
|
||||
logger.info(`[ZKP] Service is disabled for Tenant ${tenantId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info(`[ZKP] Tenant ${tenantId} submitted proof for ${requirementId}`);
|
||||
|
||||
// 1. 验证证明 (此处为模拟 ZKP 验证逻辑)
|
||||
// 实际场景下会调用 snarkjs 或类似的库验证 proofHash 是否符合 Circuit
|
||||
const isValid = await this.verifyZKP(proofHash, requirementId);
|
||||
|
||||
if (isValid) {
|
||||
await db(this.PROOF_TABLE)
|
||||
.insert({
|
||||
tenant_id: tenantId,
|
||||
requirement_id: requirementId,
|
||||
proof_hash: proofHash,
|
||||
is_verified: true,
|
||||
verified_at: new Date(),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
})
|
||||
.onConflict(['tenant_id', 'requirement_id'])
|
||||
.merge();
|
||||
|
||||
logger.info(`[ZKP] Proof verified for ${tenantId} - ${requirementId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟 ZKP 验证过程
|
||||
*/
|
||||
private static async verifyZKP(proofHash: string, requirementId: string): Promise<boolean> {
|
||||
// 模拟:只要不是 'invalid' 就认为通过
|
||||
return proofHash !== 'invalid_proof';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查租户是否满足特定资质
|
||||
*/
|
||||
static async checkQualification(tenantId: string, requirementId: string): Promise<boolean> {
|
||||
const proof = await db(this.PROOF_TABLE)
|
||||
.where({ tenant_id: tenantId, requirement_id: requirementId, is_verified: true })
|
||||
.first();
|
||||
|
||||
return !!proof;
|
||||
}
|
||||
}
|
||||
165
server/src/core/security/approvals/ApprovalService.ts
Normal file
165
server/src/core/security/approvals/ApprovalService.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { AuditService } from '../../../services/AuditService';
|
||||
import db from '../../../config/database';
|
||||
|
||||
export enum ApprovalStatus {
|
||||
PENDING = 'PENDING',
|
||||
APPROVED = 'APPROVED',
|
||||
REJECTED = 'REJECTED',
|
||||
EXPIRED = 'EXPIRED'
|
||||
}
|
||||
|
||||
export interface ApprovalRequest {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
requesterId: string;
|
||||
type: 'CREDENTIAL_RESET' | 'CREDENTIAL_EXPORT' | 'SYSTEM_CONFIG_CHANGE' | 'HIGH_VALUE_ORDER' | 'PRICE_CHANGE' | 'REFUND';
|
||||
resourceId: string;
|
||||
metadata: any;
|
||||
status: ApprovalStatus;
|
||||
currentStage: number;
|
||||
totalStages: number;
|
||||
approverId?: string;
|
||||
reason?: string;
|
||||
createdAt: Date;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [CORE_SEC_02] 授权审批服务 (Approval Service)
|
||||
* [ERP_APPROVE_01] 通用审批流引擎 (Approval Engine)
|
||||
* @description 管理高风险操作的多级审批流与规则引擎
|
||||
*/
|
||||
export class ApprovalService {
|
||||
private static readonly TABLE_NAME = 'cf_approval_requests';
|
||||
|
||||
/**
|
||||
* [ERP_APPROVE_01] 发起多级审批请求
|
||||
*/
|
||||
static async requestApproval(params: Omit<ApprovalRequest, 'id' | 'status' | 'createdAt' | 'expiresAt' | 'currentStage' | 'totalStages'>): Promise<string> {
|
||||
const id = `APPV-${Date.now()}`;
|
||||
|
||||
// 规则引擎:根据类型与金额设定审批层级
|
||||
let totalStages = 1;
|
||||
const metadata = params.metadata || {};
|
||||
if (params.type === 'HIGH_VALUE_ORDER' || (metadata.amount && metadata.amount > 5000)) {
|
||||
totalStages = 2; // 大额订单需两级审批 (MANAGER -> FINANCE)
|
||||
}
|
||||
|
||||
const request: ApprovalRequest = {
|
||||
...params,
|
||||
id,
|
||||
status: ApprovalStatus.PENDING,
|
||||
currentStage: 1,
|
||||
totalStages,
|
||||
createdAt: new Date(),
|
||||
expiresAt: new Date(Date.now() + 48 * 3600 * 1000) // 48小时有效
|
||||
};
|
||||
|
||||
logger.info(`[Approval] New ${totalStages}-stage request ${id} for ${params.type}`);
|
||||
|
||||
await db(this.TABLE_NAME).insert({
|
||||
id: request.id,
|
||||
tenant_id: request.tenantId,
|
||||
requester_id: request.requesterId,
|
||||
type: request.type,
|
||||
resource_id: request.resourceId,
|
||||
metadata: JSON.stringify(request.metadata),
|
||||
status: request.status,
|
||||
current_stage: request.currentStage,
|
||||
total_stages: request.totalStages,
|
||||
created_at: request.createdAt,
|
||||
expires_at: request.expiresAt
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* [ERP_APPROVE_01] 执行审批 (支持多级流转)
|
||||
*/
|
||||
static async approve(requestId: string, approverId: string, decision: 'APPROVE' | 'REJECT', reason?: string): Promise<void> {
|
||||
logger.info(`[Approval] Deciding request ${requestId}: ${decision} by ${approverId}`);
|
||||
|
||||
const request = await db(this.TABLE_NAME).where({ id: requestId }).first();
|
||||
if (!request) throw new Error('Approval request not found');
|
||||
|
||||
if (decision === 'REJECT') {
|
||||
await db(this.TABLE_NAME).where({ id: requestId }).update({
|
||||
status: ApprovalStatus.REJECTED,
|
||||
approver_id: approverId,
|
||||
reason,
|
||||
updated_at: new Date()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是批准,检查是否需要进入下一级
|
||||
if (request.current_stage < request.total_stages) {
|
||||
await db(this.TABLE_NAME).where({ id: requestId }).update({
|
||||
current_stage: request.current_stage + 1,
|
||||
approver_id: approverId, // 记录当前审批人
|
||||
updated_at: new Date()
|
||||
});
|
||||
logger.info(`[Approval] Request ${requestId} moved to stage ${request.current_stage + 1}`);
|
||||
} else {
|
||||
await db(this.TABLE_NAME).where({ id: requestId }).update({
|
||||
status: ApprovalStatus.APPROVED,
|
||||
approver_id: approverId,
|
||||
reason,
|
||||
updated_at: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
// 记录审计日志
|
||||
await AuditService.log({
|
||||
tenantId: request.tenant_id,
|
||||
traceId: `APPV-${Date.now()}`,
|
||||
userId: approverId,
|
||||
module: 'SECURITY',
|
||||
action: decision,
|
||||
resourceType: 'approval_request',
|
||||
resourceId: requestId,
|
||||
result: 'success',
|
||||
metadata: { reason, stage: request.current_stage },
|
||||
source: 'console'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表
|
||||
*/
|
||||
static async initTable() {
|
||||
const exists = await db.schema.hasTable(this.TABLE_NAME);
|
||||
if (!exists) {
|
||||
logger.info(`📦 Creating ${this.TABLE_NAME} table...`);
|
||||
await db.schema.createTable(this.TABLE_NAME, (table) => {
|
||||
table.string('id', 64).primary();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('requester_id', 64).notNullable();
|
||||
table.string('type', 64).notNullable();
|
||||
table.string('resource_id', 64).notNullable();
|
||||
table.json('metadata');
|
||||
table.string('status', 16).defaultTo('PENDING');
|
||||
table.integer('current_stage').defaultTo(1);
|
||||
table.integer('total_stages').defaultTo(1);
|
||||
table.string('approver_id', 64);
|
||||
table.string('reason', 255);
|
||||
table.timestamp('expires_at');
|
||||
table.timestamps(true, true);
|
||||
|
||||
table.index(['tenant_id', 'status']);
|
||||
});
|
||||
logger.info(`✅ Table ${this.TABLE_NAME} created`);
|
||||
} else {
|
||||
// 增量字段校验
|
||||
const hasCurrentStage = await db.schema.hasColumn(this.TABLE_NAME, 'current_stage');
|
||||
if (!hasCurrentStage) {
|
||||
await db.schema.alterTable(this.TABLE_NAME, (table) => {
|
||||
table.integer('current_stage').defaultTo(1).after('status');
|
||||
table.integer('total_stages').defaultTo(1).after('current_stage');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { VaultService } from '../../../services/VaultService';
|
||||
import { ApprovalService } from './ApprovalService';
|
||||
import db from '../../../config/database';
|
||||
|
||||
/**
|
||||
* [CORE_SEC_02] 凭据轮换服务 (Credential Rotation)
|
||||
* @description 管理凭据的到期提醒、强制轮换与安全变更
|
||||
*/
|
||||
export class CredentialRotationService {
|
||||
private static readonly ROTATION_INTERVAL_DAYS = 90;
|
||||
|
||||
/**
|
||||
* 检查到期凭据
|
||||
*/
|
||||
static async checkExpiringCredentials(): Promise<any[]> {
|
||||
const thresholdDate = new Date();
|
||||
thresholdDate.setDate(thresholdDate.getDate() - this.ROTATION_INTERVAL_DAYS);
|
||||
|
||||
const expiring = await db('cf_credential_vault')
|
||||
.where('updated_at', '<', thresholdDate)
|
||||
.orWhereNotNull('expires_at')
|
||||
.andWhere('expires_at', '<', new Date());
|
||||
|
||||
return expiring;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起受控轮换 (需审批)
|
||||
*/
|
||||
static async initiateControlledRotation(tenantId: string, credentialId: number, userId: string): Promise<string> {
|
||||
logger.info(`[Rotation] Initiating controlled rotation for ${credentialId} by user ${userId}`);
|
||||
|
||||
const approvalId = await ApprovalService.requestApproval({
|
||||
tenantId,
|
||||
requesterId: userId,
|
||||
type: 'CREDENTIAL_RESET',
|
||||
resourceId: String(credentialId),
|
||||
metadata: { action: 'ROTATE', strategy: 'FORCE_NEXT_LOGIN' }
|
||||
});
|
||||
|
||||
return approvalId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行轮换逻辑
|
||||
*/
|
||||
static async performRotation(
|
||||
context: { tenantId: string; userId: string; traceId: string },
|
||||
credentialId: number,
|
||||
newPassword: string
|
||||
): Promise<void> {
|
||||
logger.info(`[Rotation] Executing rotation for ${credentialId}`);
|
||||
await VaultService.rotateCredential(context, credentialId, newPassword);
|
||||
}
|
||||
}
|
||||
73
server/src/core/security/mTLSEngine.ts
Normal file
73
server/src/core/security/mTLSEngine.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import * as crypto from 'crypto';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
/**
|
||||
* [CORE_SEC_07] 全链路 mTLS 强制加密引擎
|
||||
* @description 模拟分布式节点间的双向 TLS 握手与证书校验,确保内部通信绝对安全
|
||||
*/
|
||||
export class mTLSEngine {
|
||||
private static CA_CERT = '---BEGIN CERTIFICATE--- CRAWLFUL_ROOT_CA ---END CERTIFICATE---';
|
||||
|
||||
/**
|
||||
* 生成临时节点证书 (模拟)
|
||||
*/
|
||||
static generateNodeCert(nodeId: string) {
|
||||
return {
|
||||
nodeId,
|
||||
cert: `---BEGIN CERTIFICATE--- ${nodeId}_CERT ---END CERTIFICATE---`,
|
||||
issuedAt: Date.now(),
|
||||
expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000,
|
||||
fingerprint: crypto.createHash('sha256').update(nodeId).digest('hex')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验对端证书
|
||||
*/
|
||||
static verifyPeerCert(cert: any): boolean {
|
||||
if (!cert || !cert.fingerprint) {
|
||||
logger.error('[mTLS] Missing peer certificate');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. 模拟 CA 签名校验
|
||||
const isValidSignature = cert.cert.includes('---BEGIN CERTIFICATE---');
|
||||
if (!isValidSignature) {
|
||||
logger.error(`[mTLS] Invalid certificate signature from node ${cert.nodeId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 模拟指纹校验
|
||||
const expectedFingerprint = crypto.createHash('sha256').update(cert.nodeId).digest('hex');
|
||||
if (cert.fingerprint !== expectedFingerprint) {
|
||||
logger.error(`[mTLS] Fingerprint mismatch for node ${cert.nodeId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 校验过期时间
|
||||
if (Date.now() > cert.expiresAt) {
|
||||
logger.error(`[mTLS] Certificate expired for node ${cert.nodeId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.debug(`[mTLS] Successfully verified node ${cert.nodeId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制执行 mTLS 校验的装饰器 (模拟)
|
||||
*/
|
||||
static secureCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
descriptor.value = function (...args: any[]) {
|
||||
const context = args[0]; // 假设第一个参数包含安全上下文
|
||||
if (!mTLSEngine.verifyPeerCert(context?.peerCert)) {
|
||||
throw new Error(`[mTLS] Forbidden: Secure call to ${propertyKey} failed verification`);
|
||||
}
|
||||
return originalMethod.apply(this, args);
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user