Перейти к содержанию

Безопасность и аутентификация

Как устроены три контура доступа в OneWallet: HMAC (сервис-к-сервису, вход в Payment Manager), JWT (Serverpod, пользователь→Auth Center) и PII-шифрование (данные клиента в покое). Плюс защита самого приложения: PIN, биометрия, auto-lock.

На какие вопросы отвечает

  • Как сервис подписывает запрос в Payment Manager? Какая именно формула HMAC?
  • Какие service-id существуют и кто что может?
  • Какой запрос идёт напрямую в PM, а какой — через Auth Center?
  • Чем шифруются персональные данные и как оператор их расшифровывает?
  • Почему в приложении PIN из 6 цифр и блокировка через 5 минут?

HMAC: сервис-к-сервису (вход в PM)

Каждый входящий в Payment Manager запрос обязан нести три заголовка. Никаких «доверенных» вызовов без подписи — даже от nginx из той же сети (см. adr/0002-single-hmac-auth.md).

Заголовок Значение
X-Service-Id идентификатор ключа, напр. auth-center
X-Timestamp UNIX-секунды; окно валидности ±60 с (защита от replay)
X-Signature hex HMAC-SHA256 над канонической строкой

Верная формула (источник — projects/payment-manager/src/auth/hmac.ts):

signingString = `${timestamp}\n${METHOD}\n${PATH}\n${sha256hex(body)}`
signature     = HMAC_SHA256(serviceSecret, signingString)   // hex

Тело хешируется как sha256(raw_body); для пустого тела — хеш пустой строки. Сравнение подписи — timing-safe (timingSafeEqual). Это не старая неверная схема {ServiceId}:{Timestamp}:{bodyhash} — её больше нет.

Пример (POST /intents, ts=1733500000, тело {"a":1}):

signingString:
1733500000
POST
/intents
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855   ← sha256 тела

X-Service-Id: auth-center
X-Timestamp:  1733500000
X-Signature:  9f1c…(hex HMAC-SHA256)

Секреты лежат в env SERVICE_SECRETS (JSON serviceId → secret), проверка — в src/auth/hmacPlugin.ts. PM делает fail-fast на старте, если нет ключей auth-center и nginx-gateway.

Список service-id (seed drizzle/seed.ts)

service-id Кто Права
nginx-gateway публичный ingress read-only: balance + history (allowedOperationTypes: [])
auth-center Auth Center (consumer) создание интентов, аккаунты
auth-center-merchant Auth Center (merchant-флоу) merchant-операции
admin-panel панель операторов админ-эндпоинты
admin-tool CLI/служебный админ-операции
exchange-webhook вебхук обмена топап/депозит

nginx routing (публичный шлюз)

nginx — единственная публичная точка входа; внутренние сервисы недоступны снаружи. Read-only PM-эндпоинты идут в PM напрямую (через auth_request к Auth Center для валидации JWT + инъекция HMAC от nginx-gateway); всё остальное проксируется в Auth Center. Полностью — см. projects/payment-manager/docs/dev/integrations/nginx.md.

Путь Куда Аутентификация
GET /api/pm/accounts/balance PM (direct) JWT (auth_request) → HMAC nginx-gateway
GET /api/pm/intents/history PM (direct) JWT (auth_request) → HMAC nginx-gateway
/api/* (login, KYC, профили, интенты, mini-app) Auth Center JWT в Auth Center; далее AC сам ходит в PM под auth-center
flowchart LR
  App[Flutter App<br/>Bearer JWT] --> NGX[nginx ingress]
  NGX -->|auth_request<br/>валидация JWT| AC[Auth Center]
  NGX -->|/api/pm/* + HMAC nginx-gateway| PM[Payment Manager]
  NGX -->|/api/* прочее| AC
  AC -->|createIntent + HMAC auth-center| PM

JWT (Serverpod)

Пользовательская аутентификация — через Auth Center на Serverpod 3.4.8. Клиент шлёт Authorization: Bearer <JWT>; nginx делегирует валидацию Auth Center директивой auth_request, при успехе пробрасывает X-User-Id в downstream. PM никогда не валидирует пользовательский JWT — он доверяет только HMAC от своих сервис-ключей. Mini-app launch использует отдельный RS256 LAUNCH_JWT (см. 03-apps.md, adr/0006-clients-and-appid.md).

PII-шифрование (данные в покое)

Реализовано в Auth Center (projects/onewallet_base/.../services/pii/). Поле user_profile.encryptedPii + флаг encryptPii. Ключи — в passwords.yaml: piiEncryptionKey, piiPhoneHashPepper, piiNatIdHashPepper. Детали решения — adr/0005-pii-encryption.md.

  • Шифрование (pii_crypto_service.dart): AES-256-GCM, пакет cryptography. Blob = [1 байт версия][12 байт nonce][ciphertext + 16 байт GCM tag].
  • Поисковые хеши (pii_hash_service.dart): HMAC-SHA256 с pepper для phone и natId — позволяет искать без расшифровки.
  • Маскирование (pii_mask_service.dart): отдаёт замаскированные значения для UI/логов.
  • Расшифровка (pii_decryption_service.dart): открытие plaintext только по явному запросу оператора — это аудируемое действие, не дефолт.

Полный поток KYC и хранение документов — 06-storage-and-kyc.md.

Защита приложения: PIN / биометрия / auto-lock

Требование НацБанка (BOT):

  • PIN — 6 цифр, обязателен.
  • Биометрия — через local_auth (отпечаток/Face ID), как альтернатива PIN.
  • Auto-lock — блокировка сессии после 5 минут неактивности.

Step-up auth (повышение уровня)

Инфраструктура есть: таблица pm.auth_policies + POST /policies/evaluate. По scope + condition возвращает требуемый уровень: NONE / PIN / OTP / BIOMETRIC / KYC_UPLIFT. Применяется к чувствительным операциям (крупные переводы, смена реквизитов).

Связанные документы