# 🔐 ПЛАН БЕЗОПАСНОСТИ API КЛЮЧЕЙ

## ТЕКУЩИЕ ПРОБЛЕМЫ БЕЗОПАСНОСТИ

1. **API ключи хранятся В ОТКРЫТОМ ВИДЕ** в БД ❌
2. **Нет истории изменений** ключей ❌
3. **Нет аудита доступа** к ключам ❌
4. **Ключи видны в GraphQL ответах** ❌

## РЕКОМЕНДУЕМАЯ АРХИТЕКТУРА

### 1. ШИФРОВАНИЕ КЛЮЧЕЙ

```typescript
// services/crypto-service.ts
import crypto from 'crypto'

export class CryptoService {
  private algorithm = 'aes-256-gcm'
  private secretKey = process.env.ENCRYPTION_KEY // 32 байта

  encrypt(text: string): { encrypted: string; iv: string; tag: string } {
    const iv = crypto.randomBytes(16)
    const cipher = crypto.createCipheriv(this.algorithm, this.secretKey, iv)

    let encrypted = cipher.update(text, 'utf8', 'hex')
    encrypted += cipher.final('hex')

    const tag = cipher.getAuthTag()

    return {
      encrypted,
      iv: iv.toString('hex'),
      tag: tag.toString('hex'),
    }
  }

  decrypt(encrypted: string, iv: string, tag: string): string {
    const decipher = crypto.createDecipheriv(this.algorithm, this.secretKey, Buffer.from(iv, 'hex'))

    decipher.setAuthTag(Buffer.from(tag, 'hex'))

    let decrypted = decipher.update(encrypted, 'hex', 'utf8')
    decrypted += decipher.final('utf8')

    return decrypted
  }
}
```

### 2. ОБНОВЛЕННАЯ СХЕМА БД

```prisma
model ApiKey {
  id               String   @id @default(cuid())
  marketplace      MarketplaceType
  encryptedKey     String   // Зашифрованный ключ
  encryptionIv     String   // Вектор инициализации
  encryptionTag    String   // Тег аутентификации
  keyHash          String   // Хеш для быстрого сравнения
  clientId         String?
  isActive         Boolean  @default(true)
  lastUsedAt       DateTime?
  expiresAt        DateTime?
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
  organizationId   String
  organization     Organization @relation(fields: [organizationId], references: [id])

  // Аудит
  createdById      String
  createdBy        User     @relation("ApiKeyCreatedBy", fields: [createdById], references: [id])
  lastModifiedById String?
  lastModifiedBy   User?    @relation("ApiKeyModifiedBy", fields: [lastModifiedById], references: [id])

  // История изменений
  auditLogs        ApiKeyAuditLog[]

  @@unique([organizationId, marketplace])
  @@index([keyHash])
  @@map("api_keys")
}

model ApiKeyAuditLog {
  id          String   @id @default(cuid())
  action      ApiKeyAction // CREATED, UPDATED, VALIDATED, USED, DEACTIVATED
  performedAt DateTime @default(now())
  performedBy String
  user        User     @relation(fields: [performedBy], references: [id])
  apiKeyId    String
  apiKey      ApiKey   @relation(fields: [apiKeyId], references: [id])
  metadata    Json?    // Дополнительная информация

  @@index([apiKeyId])
  @@index([performedBy])
  @@map("api_key_audit_logs")
}

enum ApiKeyAction {
  CREATED
  UPDATED
  VALIDATED
  USED
  DEACTIVATED
  REACTIVATED
  DELETED
}
```

### 3. БЕЗОПАСНЫЕ РЕЗОЛВЕРЫ

```typescript
// Сохранение ключа
const encrypted = cryptoService.encrypt(apiKey)
await prisma.apiKey.create({
  data: {
    organizationId: user.organization.id,
    marketplace,
    encryptedKey: encrypted.encrypted,
    encryptionIv: encrypted.iv,
    encryptionTag: encrypted.tag,
    keyHash: crypto.createHash('sha256').update(apiKey).digest('hex'),
    createdById: context.user.id,
    // Аудит
    auditLogs: {
      create: {
        action: 'CREATED',
        performedBy: context.user.id,
        metadata: { ip: context.ip, userAgent: context.userAgent },
      },
    },
  },
})

// Использование ключа
const apiKeyRecord = await prisma.apiKey.findUnique({ where: { id } })
const decryptedKey = cryptoService.decrypt(
  apiKeyRecord.encryptedKey,
  apiKeyRecord.encryptionIv,
  apiKeyRecord.encryptionTag,
)

// Логирование использования
await prisma.apiKeyAuditLog.create({
  data: {
    action: 'USED',
    apiKeyId: apiKeyRecord.id,
    performedBy: context.user.id,
    metadata: { purpose: 'statistics_fetch' },
  },
})
```

### 4. ЗАЩИТА В GRAPHQL

```typescript
// Никогда не возвращать расшифрованные ключи
type ApiKey {
  id: ID!
  marketplace: MarketplaceType!
  isActive: Boolean!
  lastUsedAt: DateTime
  createdAt: DateTime!
  # НЕ ВКЛЮЧАТЬ: apiKey, encryptedKey, encryptionIv, encryptionTag
  # Показывать только маскированную версию
  maskedKey: String! # "••••••••••1234"
}
```

### 5. ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ

```env
# .env.local
ENCRYPTION_KEY=your-32-byte-base64-encoded-key-here # Генерировать: openssl rand -base64 32
```

## ПЛАН МИГРАЦИИ

1. **Создать CryptoService**
2. **Обновить схему БД** с новыми полями
3. **Миграция существующих ключей** (зашифровать)
4. **Обновить резолверы** для работы с шифрованием
5. **Добавить аудит** всех операций
6. **Обновить UI** для показа только маскированных ключей

## ДОПОЛНИТЕЛЬНЫЕ МЕРЫ

- Ротация ключей шифрования каждые 90 дней
- Автоматическое удаление неиспользуемых ключей
- Уведомления об аномальной активности
- Rate limiting для API операций
- Двухфакторная аутентификация для изменения ключей
