245 lines
11 KiB
Markdown
245 lines
11 KiB
Markdown
# 07. Findings tracker — известные ограничения
|
||
|
||
Открытый реестр всех известных уязвимостей, дефектов и архитектурных gap. Severity по шкале:
|
||
- **блокер mainnet** — запуск в production невозможен до закрытия
|
||
- **высокий** — производственный риск, обязательно закрыть до публичного релиза
|
||
- **средний** — допустимо запустить с явным acknowledgment
|
||
- **низкий** — косметика, document-hygiene
|
||
|
||
Все findings имеют **closure path** — конкретные шаги для устранения.
|
||
|
||
---
|
||
|
||
## Закрыто в v6.5.0 (Phase 1)
|
||
|
||
### CF-1 — Swallowed catch без логирования ✅
|
||
|
||
**Класс:** error surface (Pass 7).
|
||
**Severity:** средний.
|
||
**Closure:** `commit cf1` (v6.5.0). 10/13 catch теперь логируют `Log.w(TAG, ...)`. Остаётся 3 в `Thread.sleep` (interrupt handling, acceptable per Kotlin pattern).
|
||
|
||
### CF-2 — Regex JSON parser ✅
|
||
|
||
**Класс:** parser robustness (Pass 8/10).
|
||
**Severity:** средний.
|
||
**Closure:** `commit cf2` (v6.5.0). Заменён на `org.json.JSONObject` с `optDouble/optString/optBoolean` и fail-safe NaN-проверкой.
|
||
|
||
### CF-3 — BIP39 wordlist integrity check ✅
|
||
|
||
**Класс:** integrity / supply chain (Pass 0 [C-9]).
|
||
**Severity:** **блокер mainnet** до закрытия.
|
||
**Closure:** `commit cf3` (v6.5.0). Hardcoded SHA-256 `2f5eed53...dbda` в `MainActivity.bip39()`. Fail-closed: возвращает пустую строку → JS бросает «bip39 list size: 0» → создание кошелька невозможно.
|
||
|
||
### CF-5 — balances.json race condition ✅
|
||
|
||
**Класс:** concurrency (Pass 8).
|
||
**Severity:** средний.
|
||
**Closure:** `commit cf5` (v6.5.0). Atomic `with_state_lock()` через отдельный `.lock` файл с единым `LOCK_EX` на read+modify+write.
|
||
|
||
### CF-7 — silent JSON swallow на backend ✅
|
||
|
||
**Класс:** error surface (Pass 7).
|
||
**Severity:** низкий.
|
||
**Closure:** `commit cf7` (v6.5.0). `force=True` + явный `400 invalid json` с detail.
|
||
|
||
### F-1 — Stale `assigned_node` поле ✅
|
||
|
||
**Класс:** consistency drift (Pass 13).
|
||
**Severity:** низкий.
|
||
**Closure:** `commit f1` (v6.5.0). Удалены `assigned_node`/`assigned_ts` поля из всех записей balances.json.
|
||
|
||
### F-3 (частично) — anon-bloat в balances.json ✅
|
||
|
||
**Класс:** state lifecycle (Pass 18 / [I-14]).
|
||
**Severity:** средний.
|
||
**Partial closure:** `commit f3` (v6.5.0). `/api/vpn/admin/purge` endpoint удаляет inactive 30+ days с balance=0. Manual trigger.
|
||
**Остаётся open:** automated daily cron (см. ниже F-3.1).
|
||
|
||
---
|
||
|
||
## Открытые findings
|
||
|
||
### F-2 — Heartbeat без cryptographic authentication ⛔
|
||
|
||
**Класс:** authentication / Sybil (Pass 19, Pass 22).
|
||
**Severity:** **блокер mainnet**.
|
||
|
||
**Поверхность:** `POST /api/vpn/heartbeat` принимает `{"address": "<40-hex>"}` и проверяет только `request.remote_addr ∈ MONTANA_NODES`. Никакой подписи владельца адреса не требуется.
|
||
|
||
**Атака:**
|
||
1. Атакующий поднимает Montana VPN клиент (открытый VLESS link).
|
||
2. Генерирует 10⁶ случайных 40-hex адресов.
|
||
3. Шлёт 10⁶ heartbeat каждые 5 секунд через VPN.
|
||
4. Backend начисляет 0.001 × 5 × 10⁶ = 5000 Ɉ/период на fake-адреса.
|
||
|
||
**Defense сейчас:** нет.
|
||
|
||
**Closure path:**
|
||
1. **Phase 3 (M-VPN-3):** Falcon-512 keypair generation в Android — каждый кошелёк имеет `(pubkey, secretkey)` от seed.
|
||
2. Heartbeat body расширяется: `{address, window_index, exit_node, signature}` где signature — Falcon-512 над `(address || window_index || exit_node)`.
|
||
3. Backend хранит pubkey per address (заполняется при первом heartbeat) → проверяет signature над всеми последующими.
|
||
4. Atomic operation: верификация подписи → начисление credit.
|
||
|
||
**Cost estimate:** 2 недели (JNI bridge для `pqcrypto-falcon` + key derivation от BIP39 seed + backend signature verify).
|
||
|
||
---
|
||
|
||
### F-4 — balances.json НЕ реплицируется на узлы Montana ⛔
|
||
|
||
**Класс:** persistence / SPOF.
|
||
**Severity:** **блокер mainnet**.
|
||
|
||
**Поверхность:** balance state живёт **только** в `/var/lib/montana-vpn-balance/balances.json` на Moscow. Если Moscow упадёт безвозвратно (диск, hosting termination, государственный захват) — все начисленные Ɉ потеряны.
|
||
|
||
**Что я ошибочно заявлял раньше:** «всё сохраняется на узлах через `montana-orchestrator.service`». **Неверно** — orchestrator занимается регистрацией узлов, не балансами.
|
||
|
||
**Closure path:**
|
||
1. **Phase 2 (M-VPN-1):** реплицировать balances.json на Helsinki + Frankfurt + NewYork через `rsync` daily. Mitigation, не fix.
|
||
2. **Phase 3 (M-VPN-2):** интегрировать с TimeChain — heartbeat становится подписанной операцией `0x06 VpnHeartbeat` → попадает в proposal → cemented через консенсус → AccountTable обновляется на ВСЕХ узлах. Authoritative persistence.
|
||
|
||
**Cost estimate:** Phase 2 — 3 дня. Phase 3 — 2 недели (новый opcode в спеке + mt-account изменения + montana-node integration).
|
||
|
||
---
|
||
|
||
### F-6 — Seed в plain text без шифрования
|
||
|
||
**Класс:** privacy at rest (Pass 19).
|
||
**Severity:** высокий (на rooted устройствах).
|
||
|
||
**Поверхность:** `localStorage.setItem("m.seed", "ranch basket resource ...")` в WebView Local Storage leveldb.
|
||
|
||
**Атаки:**
|
||
- Root доступ к `/data/data/quest.montana.vpn/app_webview/Default/Local Storage/leveldb/` → читает плоский текст
|
||
- Forensic image устройства → offline analysis
|
||
- Backup через ADB → seed в backup
|
||
|
||
**Closure path:**
|
||
- User passcode + PBKDF2(passcode → AES key) → wrap seed AES-GCM
|
||
- Или Android Keystore (hardware-backed) для wrap
|
||
|
||
**Cost estimate:** 1 неделя (UX flow + crypto + миграция existing wallets).
|
||
|
||
---
|
||
|
||
### F-7 — Backup confirmation отсутствует
|
||
|
||
**Класс:** UX-as-security.
|
||
**Severity:** высокий (вероятная потеря денег пользователями).
|
||
|
||
**Поверхность:** см. `06-Восстановление.md` §6.
|
||
|
||
**Closure path:** новый экран `s-backup-verify` между `s-auth` и `s-main` при создании.
|
||
|
||
**Cost estimate:** 1 день.
|
||
|
||
---
|
||
|
||
### F-8 — BIP44 несовместимость для cross-wallet import
|
||
|
||
**Класс:** narrative-model divergence.
|
||
**Severity:** средний (UX).
|
||
|
||
**Поверхность:** см. `06-Восстановление.md` §5. 24 слова Montana → импорт в Trust/MetaMask показывает пустые балансы.
|
||
|
||
**Closure path** (выбор):
|
||
- A. Добавить UI warning при показе seed
|
||
- B. Apply for SLIP-44 coin type + переход на BIP44 derivation path → cross-wallet import работает
|
||
|
||
Option A — 30 минут. Option B — недели (SLIP-44 registration + backward-incompatible breaking change существующих addresses).
|
||
|
||
---
|
||
|
||
### CF-4 — Reality keys + UUID в 19 местах ([C-1] SSOT violation)
|
||
|
||
**Класс:** SSOT.
|
||
**Severity:** средний.
|
||
|
||
**Поверхность:** см. `04-Сетевой-слой.md` §15.
|
||
|
||
**Closure path:** Phase 2 — Rust crate `mt-vpn-config-gen` читает keys из `mt-genesis::ProtocolParams::reality_keys`, генерирует все 19 конфигов.
|
||
|
||
**Cost estimate:** 3 дня.
|
||
|
||
---
|
||
|
||
### CF-6 — `https://montana.local/` synthetic baseURL
|
||
|
||
**Класс:** production naming [C-12].
|
||
**Severity:** низкий.
|
||
|
||
**Closure:** переименовать в `https://montana.quest/app/` (синтетический, naружу не идёт). 5 минут.
|
||
|
||
---
|
||
|
||
### F-3.1 — Automated purge cron отсутствует
|
||
|
||
**Класс:** state lifecycle (Pass 18).
|
||
**Severity:** низкий.
|
||
|
||
**Closure:** добавить systemd timer `montana-vpn-balance-purge.timer` daily + автоматический вызов в самом heartbeat (раз в 1000 операций). 1 час.
|
||
|
||
---
|
||
|
||
### CF-recovery-tests — Recovery E2E test отсутствует
|
||
|
||
**Класс:** Pass 20 user recovery trace.
|
||
**Severity:** высокий.
|
||
|
||
**Поверхность:** см. `06-Восстановление.md` §4. Нет automated test что mnemonic → adr воспроизводимо между устройствами.
|
||
|
||
**Closure path:**
|
||
- Add `BIP39DerivationTest.kt` unit test (JVM) — берёт известный mnemonic, проверяет адрес против hardcoded expected hex
|
||
- Add `RecoveryE2ETest.kt` instrumented test — то же, но в реальном WebView
|
||
|
||
**Cost estimate:** 2 дня.
|
||
|
||
---
|
||
|
||
### F-reality-pq — Reality X25519 не пост-квантово
|
||
|
||
**Класс:** [I-1] PQ-secure compliance.
|
||
**Severity:** низкий (на горизонте 10 лет).
|
||
|
||
**Поверхность:** Reality использует X25519 для key exchange — уязвим к квантовому компьютеру (Shor).
|
||
|
||
**Closure path:** upstream XTLS реализация PQ-Reality (work in progress, не наша работа). На горизонте 5-10 лет (CRQC threat model).
|
||
|
||
---
|
||
|
||
### F-keystore-backup — Genesis keystore без backup procedure
|
||
|
||
**Класс:** operational security.
|
||
**Severity:** высокий.
|
||
|
||
**Поверхность:** `/Users/kh./Python/Ничто/Montana/Android/keystore/montana.keystore` хранится только на устройстве автора. Компрометация = malicious APK подписи.
|
||
|
||
**Closure path:** HSM или Shamir-split keystore на N доверенных лиц с threshold.
|
||
|
||
---
|
||
|
||
## Roadmap закрытия
|
||
|
||
### Phase 2 (M-VPN-1, ~1-2 недели) — Rust backend
|
||
|
||
- Заменить Flask `app.py` на Rust `mt-vpn-balance` (axum + sqlx + SQLite WAL)
|
||
- F-4 mitigation: rsync replication на 3 узла Montana
|
||
- CF-4 closure: `mt-vpn-config-gen` бинарь читает Reality keys из единого источника
|
||
- F-3.1: cron purge
|
||
- CF-recovery-tests: unit + instrumented tests
|
||
- F-7: backup verify screen
|
||
- F-8: UI warning
|
||
|
||
### Phase 3 (M-VPN-2/3, ~3-4 недели) — TimeChain integration
|
||
|
||
- F-2 + F-4 final closure: новый opcode `0x06 VpnHeartbeat` в `Montana Protocol v35.26`
|
||
- mt-account реализация apply_vpn_heartbeat (50+ тестов)
|
||
- montana-node accepts heartbeat в mempool → cemented через consensus
|
||
- AccountRecord.balance обновляется через canonical apply pipeline
|
||
- Android JNI bridge для Falcon-512
|
||
- F-6 closure: encrypted seed через user passcode
|
||
|
||
### Phase 4 (опциональная, поздно) — Hardening
|
||
|
||
- F-reality-pq: переход на PQ-Reality когда XTLS upstream готов
|
||
- F-keystore-backup: HSM или Shamir split
|