Документ описывает меры безопасности, реализованные в проекте: защита API, хранение ключей бирж и общая архитектура безопасности.
- Access Token: срок жизни 15 минут, алгоритм HS256
- Refresh Token: срок жизни 7 дней, хранится в PostgreSQL как bcrypt-хеш
- Все маршруты
/api/*защищены middleware, кроме/api/auth/*и/api/health
- Требуется заголовок
Authorization: Bearer <token> - При истечении access token клиент использует refresh token для получения новой пары
- Refresh-токены хранятся только в виде хеша (bcrypt), нельзя восстановить исходный токен из БД
- Хеширование: bcrypt с 12 раундами
- Пароль не хранится в открытом виде, в БД — только
password_hash - Валидация при регистрации: минимум 8 символов, максимум 128
- Каждый запрос привязан к
userIdиз JWT - Доступ к аккаунтам бирж, позициям и ордерам ограничен данными текущего пользователя
exchange_accounts,manual_positions,trade_tasksфильтруются поuser_id
Выделенный микросервис — единственное место в системе, где хранится и используется мастер-ключ шифрования.
- Алгоритм: AES-256-GCM (аутентифицированное шифрование)
- IV: 12 случайных байт на каждое шифрование
- MASTER_ENCRYPTION_KEY: 64 hex-символа (32 байта), передаётся только в key-service через
MASTER_ENCRYPTION_KEY
В PostgreSQL в таблице exchange_accounts хранятся:
encrypted_credentials— зашифрованный JSON сapiKey,apiSecret,authTokenи т.п.encryption_iv— инициализационный векторencryption_tag— тег аутентификации GCM
Ключи в открытом виде в БД не хранятся.
- node-service не имеет прямого доступа к мастер-ключу
- Для дешифрования credentials используется HTTP-клиент (
kms-client.ts), который вызывает key-service - trading-worker при исполнении ордеров запрашивает расшифровку у key-service по
accountId - Credentials передаются по внутренней сети и не сохраняются в логах
- Порт key-service не пробрасывается на хост — доступ только из Docker-сети
- Доступ к эндпоинтам key-service защищён внутренним токеном
KMS_INTERNAL_TOKEN - Заголовок
x-internal-tokenобязателен при вызове/encrypt,/decrypt,/decrypt-raw(если токен задан) - В dev-режиме, если
KMS_INTERNAL_TOKENне задан, доступ разрешён для упрощения разработки
| Маршрут | Лимит | Ключ |
|---|---|---|
| Общие API-запросы | 100/мин | userId |
Торговые операции (/api/orders) |
10/мин | userId |
Аутентификация (/api/auth/*) |
20/мин | IP |
- Redis (prefixes:
rl:general,rl:trading,rl:auth) - Окно: 60 секунд
- При недоступности Redis лимиты не применяются (graceful degradation)
События записываются в таблицу audit_logs:
- Регистрация (
register) - Вход (
login) - Добавление аккаунта биржи (
add_account) - Удаление аккаунта (
delete_account)
Содержимое: user_id, action, details (JSON), ip_address, created_at.
- Пароли и токены не логируются
- Расшифрованные credentials не пишутся в логи
- В логах trading-worker — только
taskId,exchange,symbol, без передаваемых объёмов и цен в явном виде
- API (node-service) — аутентификация, валидация, маршрутизация; не выполняет ордера напрямую
- trading-worker — единственный компонент, выполняющий ордера на биржах; получает credentials только через key-service
- key-service — только шифрование и дешифрование; не участвует в логике торговли
- ws-service — стриминг цен и позиций; не имеет доступа к ключам
- NATS JetStream для очереди ордеров — сообщения содержат
accountId(UUID), а не сами ключи - Внутренние HTTP-вызовы (node-service → key-service, trading-worker → key-service) — только внутри Docker-сети
- JWT_SECRET — надёжный случайный секрет длиной не менее 32 байт
- MASTER_ENCRYPTION_KEY — 64 hex-символа, хранить в secrets manager, не в репозитории
- KMS_INTERNAL_TOKEN — задать для production; без него key-service доступен любому сервису в сети
- POSTGRES_PASSWORD — сильный пароль
- HTTPS — использовать Nginx с SSL/TLS; для production — валидный сертификат
- Firewall — не открывать порты key-service, postgres, redis, nats наружу, если это не требуется
┌─────────────┐ JWT ┌─────────────┐ accountId ┌──────────────┐
│ Dashboard │ ───────────► │ node-service│ ──────────────► │ NATS │
└─────────────┘ └─────────────┘ └──────┬───────┘
│ │
│ kmsEncrypt (при добавлении аккаунта) │ trades.open/close
▼ ▼
┌─────────────┐ ┌──────────────┐
│ key-service │ ◄── decrypt (accountId) ─── │trading-worker│
│ MASTER_KEY │ └──────────────┘
└──────┬──────┘
│
│ читает encrypted_credentials из PostgreSQL
▼
┌─────────────┐
│ PostgreSQL │ (encrypted_credentials, iv, tag)
└─────────────┘
Документ актуален на дату последнего обновления.