Chuyển đến nội dung chính

第 24 課:安全性與合規性 — AI 聊天機器人平台的端到端安全性

端對端加密、稽核日誌記錄、GDPR/HIPAA 合規性、AI 系統滲透測試、即時注入防禦、資料保留策略。

🏗️ 建築 — 第 24 課 第 24 課:安全性與合規性 — 安全性 端到端的人工智慧聊天機器人平台

企業人工智慧聊天機器人平台架構-從原型到生產

第 7 部分:基礎設施、安全與生產

亞洲開發網

1. AI聊天機器人威脅模型

AI聊天機器人平台有 更廣泛的攻擊面 傳統應用程式-因為透過 LLM(即時注入、資料擷取、模型操作)產生額外的攻擊向量。


┌─────────── AI CHATBOT THREAT MODEL ───────────────────┐
│                                                       │
│  EXTERNAL THREATS              INTERNAL THREATS        │
│  ┌──────────────┐              ┌──────────────┐       │
│  │ Prompt       │              │ Data leakage │       │
│  │ Injection    │              │ between      │       │
│  │              │              │ tenants      │       │
│  ├──────────────┤              ├──────────────┤       │
│  │ PII          │              │ Unauthorized │       │
│  │ Extraction   │              │ model access │       │
│  ├──────────────┤              ├──────────────┤       │
│  │ Jailbreak    │              │ Audit log    │       │
│  │ Attempts     │              │ tampering    │       │
│  ├──────────────┤              ├──────────────┤       │
│  │ DDoS on      │              │ Insider      │       │
│  │ inference    │              │ threat       │       │
│  ├──────────────┤              ├──────────────┤       │
│  │ Model        │              │ Supply chain │       │
│  │ poisoning    │              │ (malicious   │       │
│  │ via RAG      │              │  model)      │       │
│  └──────────────┘              └──────────────┘       │
│                                                       │
│  ┌─────────────────────────────────────────────────┐   │
│  │              DEFENSE LAYERS                     │   │
│  │  1. Network (WAF, TLS, mTLS)                    │   │
│  │  2. Authentication (OAuth2, API keys)           │   │
│  │  3. Authorization (RBAC, ABAC, tenant isolation)│   │
│  │  4. AI Safety (Guardrails, input validation)    │   │
│  │  5. Data Protection (encryption, masking)       │   │
│  │  6. Audit (immutable logging, SIEM)             │   │
│  └─────────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────┘

2. 端對端加密


class ConversationEncryption {
  // Encrypt conversation data at rest
  async encryptConversation(
    conversation: Conversation,
    tenantKey: CryptoKey,
  ): Promise<EncryptedConversation> {
    // Per-conversation data encryption key (DEK)
    const dek = await crypto.subtle.generateKey(
      { name: 'AES-GCM', length: 256 },
      true,
      ['encrypt', 'decrypt'],
    );

    // Encrypt conversation content with DEK
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const plaintext = new TextEncoder().encode(
      JSON.stringify(conversation.messages),
    );
    const ciphertext = await crypto.subtle.encrypt(
      { name: 'AES-GCM', iv },
      dek,
      plaintext,
    );

    // Wrap DEK with tenant's KEK (Key Encryption Key)
    const wrappedDEK = await crypto.subtle.wrapKey(
      'raw',
      dek,
      tenantKey,
      { name: 'AES-KW' },
    );

    return {
      id: conversation.id,
      tenantId: conversation.tenantId,
      encryptedMessages: Buffer.from(ciphertext).toString('base64'),
      iv: Buffer.from(iv).toString('base64'),
      wrappedKey: Buffer.from(wrappedDEK).toString('base64'),
      metadata: {
        // Metadata is NOT encrypted — for indexing
        createdAt: conversation.createdAt,
        userId: conversation.userId,
        messageCount: conversation.messages.length,
      },
    };
  }

  async decryptConversation(
    encrypted: EncryptedConversation,
    tenantKey: CryptoKey,
  ): Promise<Conversation> {
    // Unwrap DEK
    const dek = await crypto.subtle.unwrapKey(
      'raw',
      Buffer.from(encrypted.wrappedKey, 'base64'),
      tenantKey,
      { name: 'AES-KW' },
      { name: 'AES-GCM', length: 256 },
      false,
      ['decrypt'],
    );

    // Decrypt messages
    const plaintext = await crypto.subtle.decrypt(
      {
        name: 'AES-GCM',
        iv: Buffer.from(encrypted.iv, 'base64'),
      },
      dek,
      Buffer.from(encrypted.encryptedMessages, 'base64'),
    );

    const messages = JSON.parse(
      new TextDecoder().decode(plaintext),
    );

    return {
      id: encrypted.id,
      tenantId: encrypted.tenantId,
      userId: encrypted.metadata.userId,
      messages,
      createdAt: encrypted.metadata.createdAt,
    };
  }
}

3. 不可變的稽核日誌記錄


class AIAuditLogger {
  constructor(
    private readonly store: AuditStore,
    private readonly hasher: HashChain,
  ) {}

  async logEvent(event: AuditEvent): Promise<void> {
    // Hash chain — each entry links to previous (tamper-evident)
    const previousHash = await this.hasher.getLastHash(event.tenantId);

    const entry: AuditEntry = {
      id: crypto.randomUUID(),
      timestamp: new Date().toISOString(),
      tenantId: event.tenantId,
      userId: event.userId,
      action: event.action,
      resource: event.resource,
      details: this.sanitizeDetails(event.details),
      previousHash,
      hash: '', // Computed below
    };

    // Compute hash of this entry (includes previous hash for chaining)
    entry.hash = await this.hasher.computeHash(entry);

    await this.store.append(entry);
  }

  // AI-specific audit events
  async logInference(event: InferenceAuditEvent): Promise<void> {
    await this.logEvent({
      tenantId: event.tenantId,
      userId: event.userId,
      action: 'ai.inference',
      resource: `model:${event.model}`,
      details: {
        model: event.model,
        inputTokens: event.inputTokens,
        outputTokens: event.outputTokens,
        latencyMs: event.latencyMs,
        promptHash: await this.hasher.hashContent(event.prompt),
        responseHash: await this.hasher.hashContent(event.response),
        guardrailTriggered: event.guardrailTriggered,
        piiDetected: event.piiDetected,
        costUsd: event.costUsd,
      },
    });
  }

  async logDataAccess(event: DataAccessAuditEvent): Promise<void> {
    await this.logEvent({
      tenantId: event.tenantId,
      userId: event.userId,
      action: 'data.access',
      resource: `document:${event.documentId}`,
      details: {
        documentId: event.documentId,
        accessType: event.accessType, // 'read' | 'search' | 'rag_retrieval'
        query: event.query ? await this.hasher.hashContent(event.query) : null,
        chunksRetrieved: event.chunksRetrieved,
      },
    });
  }

  private sanitizeDetails(
    details: Record<string, unknown>,
  ): Record<string, unknown> {
    // Redact sensitive fields
    const sensitiveKeys = ['password', 'token', 'secret', 'apiKey'];
    const sanitized = { ...details };
    for (const key of Object.keys(sanitized)) {
      if (sensitiveKeys.some(sk => key.toLowerCase().includes(sk))) {
        sanitized[key] = '[REDACTED]';
      }
    }
    return sanitized;
  }
}

4. GDPR 與資料隱私合規性


class GDPRComplianceManager {
  // Right to be forgotten (Article 17)
  async handleDeletionRequest(
    userId: string,
    tenantId: string,
  ): Promise<DeletionReport> {
    const report: DeletionReport = {
      userId,
      requestedAt: new Date(),
      deletedItems: [],
      retainedItems: [],
    };

    // 1. Delete conversations
    const conversations = await this.conversationStore.findByUser(
      userId,
      tenantId,
    );
    for (const conv of conversations) {
      await this.conversationStore.delete(conv.id);
      report.deletedItems.push({
        type: 'conversation',
        id: conv.id,
        deletedAt: new Date(),
      });
    }

    // 2. Delete from vector store (RAG memories)
    await this.vectorStore.deleteByFilter({
      tenantId,
      userId,
    });
    report.deletedItems.push({
      type: 'vector_embeddings',
      count: conversations.length,
      deletedAt: new Date(),
    });

    // 3. Delete user profile and preferences
    await this.userStore.delete(userId);
    report.deletedItems.push({
      type: 'user_profile',
      id: userId,
      deletedAt: new Date(),
    });

    // 4. Retain audit logs (legal basis: legitimate interest)
    report.retainedItems.push({
      type: 'audit_logs',
      reason: 'Legal obligation — retained for compliance audit (Art. 17(3)(b))',
      retentionPeriod: '7 years',
    });

    // 5. Log the deletion itself
    await this.auditLogger.logEvent({
      tenantId,
      userId: 'system',
      action: 'gdpr.deletion',
      resource: `user:${userId}`,
      details: {
        deletedConversations: conversations.length,
        retainedAuditLogs: true,
      },
    });

    return report;
  }

  // Data export (Article 20 — Right to Data Portability)
  async exportUserData(
    userId: string,
    tenantId: string,
  ): Promise<DataExport> {
    const [conversations, profile, preferences] = await Promise.all([
      this.conversationStore.findByUser(userId, tenantId),
      this.userStore.get(userId),
      this.preferenceStore.get(userId),
    ]);

    return {
      exportedAt: new Date(),
      format: 'JSON',
      data: {
        profile,
        preferences,
        conversations: conversations.map(c => ({
          id: c.id,
          createdAt: c.createdAt,
          messages: c.messages.map(m => ({
            role: m.role,
            content: m.content,
            timestamp: m.timestamp,
          })),
        })),
      },
    };
  }
}

5. 資料保留政策


class DataRetentionManager {
  private readonly policies: RetentionPolicy[] = [
    {
      dataType: 'conversations',
      retentionDays: 90,
      action: 'anonymize', // Keep structure, remove PII
    },
    {
      dataType: 'conversations',
      retentionDays: 365,
      action: 'delete',
    },
    {
      dataType: 'inference_logs',
      retentionDays: 30,
      action: 'aggregate', // Keep stats, delete raw prompts
    },
    {
      dataType: 'audit_logs',
      retentionDays: 2555, // ~7 years
      action: 'archive', // Move to cold storage
    },
    {
      dataType: 'vector_embeddings',
      retentionDays: 180,
      action: 'delete',
    },
  ];

  async enforceRetention(tenantId: string): Promise<RetentionReport> {
    const report: RetentionReport = { tenantId, actions: [] };

    for (const policy of this.policies) {
      const cutoff = new Date();
      cutoff.setDate(cutoff.getDate() - policy.retentionDays);

      switch (policy.action) {
        case 'anonymize': {
          const count = await this.anonymizeOlderThan(
            policy.dataType, tenantId, cutoff,
          );
          report.actions.push({
            policy: `${policy.dataType}:anonymize`,
            affectedRecords: count,
          });
          break;
        }
        case 'delete': {
          const count = await this.deleteOlderThan(
            policy.dataType, tenantId, cutoff,
          );
          report.actions.push({
            policy: `${policy.dataType}:delete`,
            affectedRecords: count,
          });
          break;
        }
        case 'aggregate': {
          const count = await this.aggregateOlderThan(
            policy.dataType, tenantId, cutoff,
          );
          report.actions.push({
            policy: `${policy.dataType}:aggregate`,
            affectedRecords: count,
          });
          break;
        }
        case 'archive': {
          const count = await this.archiveOlderThan(
            policy.dataType, tenantId, cutoff,
          );
          report.actions.push({
            policy: `${policy.dataType}:archive`,
            affectedRecords: count,
          });
          break;
        }
      }
    }

    return report;
  }
}

6. RBAC 和 API 安全


// Role-Based Access Control for AI Platform
const AI_PLATFORM_ROLES = {
  'chatbot-user': {
    permissions: [
      'conversation:create',
      'conversation:read:own',
      'knowledge:search',
    ],
  },
  'chatbot-admin': {
    permissions: [
      'conversation:read:all',
      'knowledge:manage',
      'prompt:manage',
      'guardrail:manage',
      'analytics:read',
      'workflow:manage',
    ],
  },
  'tenant-owner': {
    permissions: [
      'tenant:manage',
      'billing:manage',
      'user:manage',
      'api-key:manage',
      'audit:read',
      'model:configure',
    ],
  },
  'platform-admin': {
    permissions: [
      'tenant:create',
      'model:deploy',
      'gpu:manage',
      'system:configure',
      'audit:read:all',
    ],
  },
} as const;

// API Key with scoped permissions
class APIKeyManager {
  async createAPIKey(
    tenantId: string,
    name: string,
    permissions: string[],
    expiresAt?: Date,
  ): Promise<APIKey> {
    const rawKey = crypto.randomBytes(32).toString('base64url');
    const prefix = `xai_${tenantId.substring(0, 8)}`;
    const fullKey = `${prefix}_${rawKey}`;

    // Store only the hash
    const keyHash = await this.hashKey(fullKey);

    const apiKey: APIKey = {
      id: crypto.randomUUID(),
      tenantId,
      name,
      keyHash,
      keyPrefix: fullKey.substring(0, 12), // For identification
      permissions,
      expiresAt: expiresAt ?? new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
      createdAt: new Date(),
      lastUsedAt: null,
    };

    await this.store.save(apiKey);

    // Return full key ONLY on creation (never stored/logged)
    return { ...apiKey, rawKey: fullKey };
  }
}

第 24 課總結

  • 威脅模型:AI聊天機器人透過提示注入、PII提取、模型中毒等方式增加攻擊面
  • 加密:用於對話資料的 AES-256-GCM,金鑰包裝(DEK/KEK 模式)
  • 審計日誌記錄:哈希鏈不可變日誌、AI 特定事件(推理、資料存取)
  • 一般資料保護條例:刪除權(第 17 條)、資料可攜性(第 20 條)、保留政策
  • RBAC:4 個角色(使用者 → 平台管理員),具有前綴標識的範圍 API 金鑰

下一篇: 案例研究-真實的企業人工智慧聊天機器人實施、架構決策、經驗教訓、投資報酬率分析。