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

Внутренние комментарии — BOT 18/2568

Документ: рабочие заметки по чеклисту соответствия BOT 18/2568 Аудитория: команда OneWallet (внутренний) Дата: 2026-04-23 Связанный файл: 01-response.md

Формат: для каждого пункта — что есть в проекте, где это в коде/доках, что не хватает, возможные решения с оценкой трудозатрат.


1.1 Не отправлять ссылки в SMS / email для верификации личности

Текущее состояние: - OTP отправляется только по явному запросу пользователя (регистрация, логин, сброс пароля). - SMTP-шаблоны в projects/onewallet_base/onewallet_base_server/lib/src/mail/ содержат только коды. - Маркетинговых рассылок с clickable identity-verification links нет.

Gap: нет письменно зафиксированной политики и явного уведомления пользователя.

Решения: 1. Добавить раздел «Communication Policy» в docs/11-security-policy.md. Трудозатраты: 2 часа. 2. Обновить email-шаблоны — добавить фразу «We never ask you to click a link to verify your identity». Трудозатраты: 4 часа. 3. Добавить in-app баннер в onewallet_base_flutter/lib/screens/settings/ или на Welcome screen. Трудозатраты: 4 часа.

Итого: ~1 рабочий день.


1.2 Процесс мониторинга и реакции на поддельные приложения

Текущее состояние: отсутствует.

Gap: всё — назначение ответственного, контракт со сторонами, runbook.

Решения: 1. Назначить Brand/Security officer (организационный шаг). 2. Подключить Google Play Protect takedown flow + Apple App Store Brand Protection (требует DUNS number и подписи от юр. лица). 3. Настроить ежедневный мониторинг: - Google Custom Search API по ключам "OneWallet", "IPPS Pay", "OneWallet Thailand APK". - Мониторинг APKMirror, APKPure, Uptodown. - Мониторинг Facebook / TikTok рекламы с логотипом. 4. Runbook инцидента в docs/runbooks/fake-app-response.md: - Обнаружение → legal send takedown → публикация предупреждения на сайте + in-app banner → force-logout всех сессий без валидного attestation (связан с 2.6). 5. Логирование: событие security.fake_app.detected в Unified Log (docs/13-logging.md).

Трудозатраты: 3–5 дней (инженерия) + 1–2 недели (юр. оформление партнёрств).


1.3 Один пользователь на устройство

Текущее состояние: - JWT/refresh в flutter_secure_storage. - Нет связи user_id ↔ device_id в БД. - Переключение аккаунтов не отслеживается.

Gap: полный.

Решения:

Модель данных

# projects/onewallet_base/onewallet_base_server/lib/src/models/user_device.spy.yaml
class: UserDevice
table: user_device
fields:
  userId: int, relation(parent=user)
  deviceId: String   # стабильный id от клиента (KeyStore/Keychain)
  platform: String   # ios / android
  model: String?
  osVersion: String?
  attestationValid: bool, default=false
  firstSeenAt: DateTime
  lastSeenAt: DateTime
  revokedAt: DateTime?
indexes:
  user_device_device_idx:
    fields: deviceId

API

  • AuthEndpoint.registerDevice({deviceId, platform, ...}) — вызывается после login/register.
  • AuthEndpoint.listMyDevices() — список устройств в профиле.
  • AuthEndpoint.revokeDevice(deviceId) — выход из конкретного устройства.
  • В JWT добавить claim device_id.

Policy

  • 1 активный user_id на device_id (если пытается залогиниться другой — предыдущий force logout).
  • N активных устройств на пользователя (конфиг, default N=3).
  • При logout — revokedAt = now(), но запись не удаляем (для аудита).

Клиент (Flutter)

  • device_info_plus для model/OS.
  • Уникальный device_id:
  • Android: UUID v4, хранится в EncryptedSharedPreferences (backup disabled).
  • iOS: UUID v4, хранится в Keychain с kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly.

Трудозатраты: ~5 рабочих дней (модель + миграция + endpoints + Flutter интеграция + тесты).


1.4 Face verification + liveness для high-value транзакций

Текущее состояние: - Face match через Gemini есть только на этапе KYC (docs/03-kyc-module.md). - Liveness detection помечен P2 (docs/11-security-policy.md:318) — не в scope запуска. - Повторная верификация на высокорисковые транзакции отсутствует.

Gap: - Liveness нужно перенести в P0. - Ввести step-up authentication на high-value.

Решения:

Перевод liveness в P0

  • Обновить docs/11-security-policy.md §7.2 и docs/03-kyc-module.md:354.
  • Метод: Gemini gemini-2.5-flash с multi-frame selfie (3 фрейма, детект морганий и поворота головы).
  • Альтернатива: SDK AWS Rekognition Face Liveness (~ $0.025 за проверку) — если Gemini недостаточно.

Step-up flow

  1. PM при создании intent: если amount > step_up_thresholdstate = AWAITING_STEP_UP.
  2. Клиент получает через intentStatusStream событие step_up_required.
  3. Flutter открывает экран step-up: selfie + liveness.
  4. PaymentEndpoint.confirmStepUp(intentId, selfieUrl) → PM вызывает face-match с KYC-селфи + liveness. На success: state = VALIDATED.

Пороги (требуется согласование с compliance)

  • ≥ 50 000 THB / транзакция, или
  • ≥ 200 000 THB / 24 часа (cumulative).
  • Хранить в payment_limits.step_up_threshold (column add).

Кэш step-up

  • Успешный step-up действителен 15 минут для того же устройства (Redis).

Трудозатраты: - Liveness в KYC: 5 дней. - Step-up flow: 7 дней. - Итого: ~2,5 недели.


1.5 Дневные лимиты переводов

Текущее состояние: Completed. - docs/04-payment-manager.md §11: per-tx min/max, daily_amount, daily_count, monthly_amount. - Разделение по KYC tier (pending_operator_review vs fully_verified). - Проверка в PM на переходе CREATED → VALIDATED. - Admin UI через AdminLimitsEndpoint.

Что проверить: - Выставлены ли в БД значения, соответствующие BOT-требованиям для e-Money Tier 1 / Tier 2. - Добавить алерт: если >1% операций близко к дневному лимиту → уведомление оперкоманды.

Трудозатраты: 1 день (только alerts + ревизия значений).


2.1 Не хранить чувствительные данные на устройстве

Текущее состояние: Completed. - JWT/refresh в flutter_secure_storage (iOS Keychain / Android Keystore). - PII не кэшируется локально. - Offline-кэш (drift) предусмотрен только для баланса и агрегированной истории.

Что проверить: - Audit onewallet_base_flutter/lib/ на SharedPreferences, Hive, прямую запись в файлы — убедиться, что там нет PII. - Задокументировать whitelist допустимых для локального хранения данных в docs/01-mobile-app.md.

Трудозатраты: 0,5 дня.


2.2 Ограничение чувствительных данных на экране / блюр

Текущее состояние: отсутствует.

Gap: нет защиты от скриншотов и превью в task switcher.

Решения:

Android

  • FLAG_SECURE на экранах: PIN, KYC, Balance, Transactions, Settings.
  • Пакет flutter_windowmanager:
    FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
    
  • Устанавливать во всех sensitive-экранах через mixin.

iOS

  • Overlay view в AppDelegate.applicationWillResignActive — blur splash (logo).
  • UIScreen.main.isCaptured observer → блок UI при захвате экрана.
  • Нативный channel SecurityChannel.swift.

UX

  • Баланс скрыт по умолчанию при возврате из background → тап + биометрия раскрывает.

Трудозатраты: 3 дня.


2.3 Безопасные протоколы + шифрование

Текущее состояние: Completed.

  • TLS 1.2+ во всём контуре.
  • TLS certificate pinning на Flutter (docs/11-security-policy.md:80).
  • AES-256-GCM для PII (user_profile.encryptedPii) и OCR (kyc_verification.ocrResult) — реализовано 2026-04-24.
  • HMAC-SHA256 + pepper для детерминированного поиска по phone/nationalId.
  • bcrypt cost ≥12.
  • HMAC-SHA512 для webhook-подписей IPPS.

Что проверить: - Pin-set: primary + backup сертификаты с задокументированной ротацией (сейчас, возможно, только один). - Собрать evidence pack для аудитора: Nginx TLS config, pin-list, dio HTTPS config.

Трудозатраты: 1 день (сбор evidence + добавление backup pin).


2.4 Только необходимые разрешения

Текущее состояние: - Android: camera, USE_BIOMETRIC, USE_FINGERPRINT, POST_NOTIFICATIONS, INTERNET. - iOS: NSCameraUsageDescription, NSFaceIDUsageDescription.

Gap: нужен формальный audit — возможно, пришли лишние permissions через транзитивные Flutter-плагины.

Решения: 1. Выгрузить текущий AndroidManifest.xml и Info.plist, свериться с whitelist. 2. Убедиться, что нет WRITE_EXTERNAL_STORAGE, READ_CONTACTS, LOCATION, READ_PHONE_STATE. 3. Добавить таблицу justifications в docs/01-mobile-app.md:

Permission Назначение Где запрашивается Runtime / Install-time
camera KYC, QR-сканер Перед первым использованием Runtime
USE_BIOMETRIC Biometric login При enroll Install-time
POST_NOTIFICATIONS FCM После первого логина Runtime

Трудозатраты: 1 день.


2.5 Блокировка устаревших версий приложения

Текущее состояние: отсутствует.

Решения:

Backend

  • Таблица app_versions:
    platform: String  # ios | android
    minSupportedVersion: String  # 1.4.0
    latestVersion: String        # 1.6.2
    deprecationDate: DateTime?
    releaseNotes: String?
    storeUrl: String
    
  • Endpoint AppVersionEndpoint.check({platform, currentVersion}){status: ok|warn|forceUpdate, minSupported, latest, storeUrl}.
  • API Gateway middleware: если X-App-Version < min → 426 Upgrade Required.

Клиент (Flutter)

  • На старте приложения + каждые 6 часов — вызов check.
  • forceUpdate → full-screen block с deep link в Store.
  • warn → non-dismissable banner.

Admin

  • В Admin Panel добавить страницу /settings/app-versions для управления.

Трудозатраты: 4 дня.


2.6 Anti-tampering

Текущее состояние: отсутствует.

Решения:

Attestation

  • Android: Play Integrity API (замена SafetyNet, обязательный с 2024).
  • iOS: DeviceCheck + App Attest.

Flow

  1. Клиент при login / каждый re-auth получает nonce от сервера.
  2. Получает attestation token от Google/Apple.
  3. Отправляет SecurityEndpoint.attestDevice({token, nonce}).
  4. Сервер валидирует через Google Play Integrity / Apple App Attest API.
  5. На success — user_device.attestationValid = true (связка с 1.3).
  6. На fail — лог security.attestation.failed, блок финансовых операций до успешной повторной attestation.

Gradle / Xcode

  • Android: включить Play Integrity SDK.
  • iOS: включить App Attest entitlement.

Трудозатраты: 6 дней.


2.7 Secure session management

Текущее состояние: Completed. - JWT ES256, access TTL 15 мин, refresh TTL 30 дней с one-time rotation. - Redis sessions с TTL. - Инвалидация при logout / смене пароля / revoke. - Biometric key TTL 30–60 дней. - PIN + auto-lock 5 мин (регуляторное требование НацБанка — см. memory).

Что проверить: - Процедура экстренной ротации JWT signing key — есть ли runbook? JWKS с kid для безболезненной ротации.

Трудозатраты: 1 день (runbook + JWKS с kid, если ещё нет).


2.8 Защита исходного кода

Текущее состояние: стандартная Flutter-сборка без обфускации.

Решения:

Flutter

flutter build apk --release \
  --obfuscate \
  --split-debug-info=build/symbols/android/
flutter build ios --release \
  --obfuscate \
  --split-debug-info=build/symbols/ios/
- Хранить symbols/ в артефактах CI (для деобфускации стек-трейсов).

Android

  • R8/ProGuard включить в android/app/build.gradle:
    buildTypes {
      release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
      }
    }
    
  • proguard-rules.pro — правила для используемых библиотек.

iOS

  • Проверить STRIP_INSTALLED_PRODUCT = YES, DEPLOYMENT_POSTPROCESSING = YES в Release.
  • Убрать все print / debugPrint в release (lint rule avoid_print).

Dart-defines

  • Никаких hardcoded секретов — всё через --dart-define в CI.

Трудозатраты: 2 дня.


2.9 Блокировка rooted / jailbroken устройств

Текущее состояние: отсутствует.

Решения:

Библиотеки

  • flutter_jailbreak_detection — простая проверка.
  • freerasp (RASP) — более глубокая защита (Talsec, open source, commercial support).

Policy

  • Hard-block (P0): блок всех финансовых операций (intent creation, transfer, top-up, pay).
  • Soft-block (P1): банк-мессадж «На устройстве обнаружены признаки root/jailbreak» + ограничение лимитов.
  • Логирование: security.device.rooted.
  • В Таиланде BOT 18/2568 явно требует блок, так что hard-block оправдан.
  • В других юрисдикциях согласовать с legal (GDPR considerations).

Трудозатраты: 3 дня.


2.10 Блокировка опасных одновременно запущенных приложений

Текущее состояние: отсутствует.

Решения:

Android

  • Проверка включённых Accessibility Services через AccessibilityManager.getEnabledAccessibilityServiceList().
  • Blocklist популярных remote-доступ приложений: TeamViewer, AnyDesk, RustDesk, Splashtop, Samsung Smart Switch (remote mode).
  • Нативный channel SecurityChannel.checkConcurrentApps().
  • На экранах PIN / платёж — оверлей + предупреждение «Please close screen-sharing apps».

iOS

  • UIScreen.main.isCaptured — observer; при true → блок.
  • UIScreen.main.mirroredScreen != nil — аналогично.

Policy

  • Soft-block: предупреждение + возможность продолжить (для support-кейсов).
  • Hard-block на транзакциях ≥ step-up threshold.

Трудозатраты: 4 дня.


2.11 TB-CERT high-risk devices

Текущее состояние: отсутствует.

Решения:

Подписка

  • Зарегистрироваться в TB-CERT (Thai Banking Computer Emergency Response Team) для получения feed уязвимых моделей и версий OS. Требует юр. шаг.

Модель

# device_blocklist.spy.yaml
class: DeviceBlocklistEntry
table: device_blocklist
fields:
  platform: String  # ios | android
  modelRegex: String  # "^SM-G97.*"
  minOsVersion: String?
  reason: String
  severity: String  # hard_block | soft_warn
  source: String    # TB-CERT | internal
  updatedAt: DateTime

Flow

  • Клиент шлёт device_info_plus (model, OS) при attestation (2.6).
  • Сервер сравнивает с blocklist → hard-block или warn.
  • Feed TB-CERT импортируется через cron job раз в сутки.

Трудозатраты: - Инженерия: 4 дня. - Юр. оформление подписки TB-CERT: 2–3 недели (параллельно).


Архитектурные выводы

  1. Новый эпик: Mobile Security Hardening — объединяет пункты 1.3, 2.2, 2.5, 2.6, 2.8, 2.9, 2.10, 2.11. Логично вынести в отдельный план plans/2026-04-23/mobile-bot-compliance-hardening/.
  2. Новый модуль: device-attestation — объединяет logic для 1.3 + 2.6 + 2.9 + 2.11 (device binding, attestation, root detection, blocklist). Единая таблица user_device + device_blocklist.
  3. Новый эндпоинт: AppVersionEndpoint для 2.5.
  4. Step-up flow (1.4) — это расширение Payment Manager + KYC: новые состояния intent, новый endpoint, переиспользование KYC face-match.
  5. Документация: обновить docs/01-mobile-app.md §5 (NFR) + docs/11-security-policy.md (добавить §10 Mobile Device Security).

Оценка общего объёма

Блок Трудозатраты
Документация + политики (1.1, 2.1, 2.3, 2.7) 3 дня
Permissions audit + App version gate (2.4, 2.5) 5 дней
Mobile UI hardening (2.2, 2.8) 5 дней
Device binding + attestation + root + TB-CERT (1.3, 2.6, 2.9, 2.11) 15 дней
Concurrent apps блок (2.10) 4 дня
Liveness P0 + step-up (1.4) 12 дней
Fake apps monitoring (1.2) 5 дней + legal
Итого ~50 инженер-дней = 10 недель

Разумно параллелить 2 разработчика → ~5–6 недель до полного соответствия.


Ссылки

  • BOT Notification 18/2568 — Security of Financial and Payment Services on Mobile Devices
  • docs/11-security-policy.md — текущая политика ИБ
  • docs/01-mobile-app.md — спецификация мобильного приложения
  • docs/04-payment-manager.md §11 — система лимитов
  • docs/03-kyc-module.md — KYC и face-match
  • 03-todo.md — TODO list открытых вопросов