Безопасность и аутентификация¶
Как устроены три контура доступа в 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. Применяется к чувствительным операциям (крупные переводы, смена реквизитов).
Связанные документы¶
- adr/0002-single-hmac-auth.md — почему единый HMAC для всех вызовов в PM
- adr/0005-pii-encryption.md — решение по шифрованию PII
- compliance/bot-qa.md — вопросы-ответы регулятора BOT
- 04-payments-and-ledger.md — intent-флоу и ledger
- 02-services.md — карта сервисов