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>
This commit is contained in:
Veronika Smirnova
2025-09-18 21:31:27 +03:00
parent 3efc387308
commit b6935428ab
5 changed files with 1124 additions and 0 deletions

View File

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