
- 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>
5.9 KiB
5.9 KiB
🔐 ПЛАН БЕЗОПАСНОСТИ API КЛЮЧЕЙ
ТЕКУЩИЕ ПРОБЛЕМЫ БЕЗОПАСНОСТИ
- API ключи хранятся В ОТКРЫТОМ ВИДЕ в БД ❌
- Нет истории изменений ключей ❌
- Нет аудита доступа к ключам ❌
- Ключи видны в GraphQL ответах ❌
РЕКОМЕНДУЕМАЯ АРХИТЕКТУРА
1. ШИФРОВАНИЕ КЛЮЧЕЙ
// 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. ОБНОВЛЕННАЯ СХЕМА БД
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. БЕЗОПАСНЫЕ РЕЗОЛВЕРЫ
// Сохранение ключа
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
// Никогда не возвращать расшифрованные ключи
type ApiKey {
id: ID!
marketplace: MarketplaceType!
isActive: Boolean!
lastUsedAt: DateTime
createdAt: DateTime!
# НЕ ВКЛЮЧАТЬ: apiKey, encryptedKey, encryptionIv, encryptionTag
# Показывать только маскированную версию
maskedKey: String! # "••••••••••1234"
}
5. ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ
# .env.local
ENCRYPTION_KEY=your-32-byte-base64-encoded-key-here # Генерировать: openssl rand -base64 32
ПЛАН МИГРАЦИИ
- Создать CryptoService
- Обновить схему БД с новыми полями
- Миграция существующих ключей (зашифровать)
- Обновить резолверы для работы с шифрованием
- Добавить аудит всех операций
- Обновить UI для показа только маскированных ключей
ДОПОЛНИТЕЛЬНЫЕ МЕРЫ
- Ротация ключей шифрования каждые 90 дней
- Автоматическое удаление неиспользуемых ключей
- Уведомления об аномальной активности
- Rate limiting для API операций
- Двухфакторная аутентификация для изменения ключей