Files
sfera-new/docs/API_KEYS_SECURITY_PLAN.md
Veronika Smirnova b6935428ab docs: добавить планы по API ключам и отладке статистики
- API_KEYS_IMPLEMENTATION_PLAN.md - план реализации системы API ключей
- API_KEYS_SECURITY_PLAN.md - план безопасности API ключей
- API_KEYS_SIMPLE_PLAN.md - упрощенный план API ключей
- DEBUG_SELLER_STATISTICS.md - отладка статистики селлеров
- FIX_API_KEYS_SAVING.md - исправление сохранения API ключей

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-18 21:31:27 +03:00

193 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔐 ПЛАН БЕЗОПАСНОСТИ 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 операций
- Двухфакторная аутентификация для изменения ключей