Внутренние комментарии — 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¶
- PM при создании intent: если
amount > step_up_threshold→state = AWAITING_STEP_UP. - Клиент получает через
intentStatusStreamсобытиеstep_up_required. - Flutter открывает экран step-up: selfie + liveness.
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: - Устанавливать во всех sensitive-экранах через mixin.
iOS¶
- Overlay view в
AppDelegate.applicationWillResignActive— blur splash (logo). UIScreen.main.isCapturedobserver → блок 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: - 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¶
- Клиент при login / каждый re-auth получает
nonceот сервера. - Получает attestation token от Google/Apple.
- Отправляет
SecurityEndpoint.attestDevice({token, nonce}). - Сервер валидирует через Google Play Integrity / Apple App Attest API.
- На success —
user_device.attestationValid = true(связка с 1.3). - На 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: proguard-rules.pro— правила для используемых библиотек.
iOS¶
- Проверить
STRIP_INSTALLED_PRODUCT = YES,DEPLOYMENT_POSTPROCESSING = YESв Release. - Убрать все
print/debugPrintв release (lint ruleavoid_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.
Legal caveat¶
- В Таиланде 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 недели (параллельно).
Архитектурные выводы¶
- Новый эпик:
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/. - Новый модуль:
device-attestation— объединяет logic для 1.3 + 2.6 + 2.9 + 2.11 (device binding, attestation, root detection, blocklist). Единая таблицаuser_device+device_blocklist. - Новый эндпоинт:
AppVersionEndpointдля 2.5. - Step-up flow (1.4) — это расширение Payment Manager + KYC: новые состояния intent, новый endpoint, переиспользование KYC face-match.
- Документация: обновить
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 открытых вопросов