Користувач втратив доступ до гаманця через баг у генераторі seed-фрази: Math.random() на React Native дав передбачувану послідовність. Конкуренти не перевіряли джерело ентропії — результат: дублікати сидів і втрата коштів. Такі інциденти — звичайна справа в криптовалютних додатках. Одна така помилка може коштувати користувачам мільйони доларів, а відновлення коштів — майже нереальне завдання. Наша команда 5+ років впроваджує BIP-39 на iOS, Android та крос-платформі. Понад 20 проєктів, жодної колізії.
Корінь проблеми — небезпечний CSPRNG. Навіть досвідчені розробники плутають SecureRandom зі звичайним Random. Псевдовипадкові числа (PRNG) типу arc4random дають лише 32 біти ентропії — цього недостатньо для BIP-39. Потрібен криптостійкий генератор, сертифікований за стандартами NIST.
Генерація: де помиляються
Найнебезпечніша помилка — використання псевдовипадкового джерела. Math.random() у JavaScript не криптографічно випадковий. Date.now() як сид — катастрофа. Для BIP-39 потрібно рівно 128 біт (12 слів) або 256 біт (24 слова) криптографічно випадкової ентропії.
Нижче — порівняння джерел ентропії:
| Платформа |
Правильний генератор |
Небезпечна альтернатива |
| iOS |
SecRandomCopyBytes |
arc4random (тільки 32 біти) |
| Android |
SecureRandom |
Random (псевдовипадковий) |
| React Native |
crypto.getRandomValues (через polyfill) |
Math.random |
Для порівняння: використання SecureRandom замість Random збільшує простір ключів у 10^38 разів, роблячи атаку перебором практично неможливою.
На iOS правильний шлях – SecRandomCopyBytes:
var entropy = Data(count: 16) // 128 біт для 12 слів
let result = entropy.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, 16, $0.baseAddress!)
}
guard result == errSecSuccess else { throw WalletError.entropyGeneration }
На Android – SecureRandom із java.security, не Random:
val entropy = ByteArray(16)
SecureRandom().nextBytes(entropy)
React Native: react-native-get-random-values поліфілить crypto.getRandomValues(), який використовує нативний CSPRNG. Без цього пакету @noble/hashes та @scure/bip39 працюють на небезпечному джерелі.
Чому важливо використовувати криптографічний генератор випадкових чисел?
Використання небезпечного random може призвести до колізій (два гаманці з однаковою seed-фразою) або передбачуваності. CSPRNG забезпечує 128 біт ентропії, що робить атаку перебором неможливою в осяжному майбутньому. Економія на генераторі обертається ризиком втрати коштів. Наші інженери завжди перевіряють генератор на відповідність стандартам NIST.
Відновлення та сумісність
Відновлення – це введення 12/24 слів → ті ж адреси. Баги тут зазвичай у нормалізації тексту: зайві пробіли, unicode пробіли (NBSP замість звичайного), регістр. bip39.validateMnemonic() має повернути false для таких випадків, але UI повинен нормалізувати введення до перевірки – trim() та replace(\s+/g, ' ') обов'язкові.
Друге джерело несумісності – passphrase. BIP-39 дозволяє опціональну passphrase («25-те слово»). MetaMask її ігнорує (пустий рядок). Trezor підтримує. Якщо ваш гаманець тихо передає пусту passphrase, а користувач відновлюється на пристрої, де вказав passphrase – адреси будуть іншими. Потрібно явно запитувати при відновленні.
Як відновити seed-фразу без помилок сумісності?
Ми застосовуємо наступний покроковий алгоритм:
- Нормалізація введення (trim, lowercase, одиничні пробіли).
- Прямий запит passphrase в окремому полі.
- Перевірка контрольної суми (слово-код).
- Тестування на офіційних векторах BIP-39.
- Підтримка 12 і 24 слів.
Типові помилки при генерації seed-фрази
- Використання
Date.now() як seed
- Відсутність нормалізації введення при відновленні
- Ігнорування passphrase
- Неперевірка контрольної суми
- Копіювання seed-фрази в clipboard без очищення
UX для підтвердження seed-фрази
Показувати слова групами по 4, не всі одразу. Після показу – верифікація: випадкові 3–4 слова в довільному порядку, користувач тапає у правильній послідовності. Не давати копіювати через clipboard за замовчуванням – буфер обміну читають інші додатки. Якщо копіювання дозволено, очищати clipboard через 60 секунд через UIApplication/ClipboardManager.
На Android 10+ ClipboardManager.clearPrimaryClip() – публічний API. На iOS до 16 прямого очищення немає, встановлюємо в clipboard пустий рядок.
Процес роботи та що входить
Реалізація включає кілька етапів. Спочатку — аудит поточної системи сидів та джерел ентропії. Потім проектування безпечного коду генерації, розробка UI для показу та верифікації слів, механізм відновлення з нормалізацією. Після — тестування на офіційних векторах BIP-39 та інтеграція passphrase (опціонально). Вкінці — документація та гайд для розробника.
| Етап |
Опис |
| Аудит |
Перевірка існуючого коду на вразливості |
| Реалізація |
Написання коду генерації та відновлення |
| UI |
Інтерфейс показу та верифікації seed-фрази |
| Тестування |
Прогін на офіційних тест-векторах BIP-39 |
| Інтеграція |
Підключення passphrase та сумісність |
Терміни та гарантії
Базова реалізація — від 3 до 5 днів. Розширена (з passphrase, сумісність з конкретним гаманцем) — від 5 до 7 днів. Ми даємо гарантію коректної генерації та відновлення згідно з BIP-39 specification. Щоб уникнути типових помилок на старті, замовте аудит вашої поточної реалізації. Отримайте консультацію інженера — зв'яжіться з нами.
Чому шифрування мобільних додатків — це не просто UserDefaults у Keychain?
Ми провели аудит понад 40 мобільних додатків — і на кожному другому знаходили токени в UserDefaults, відсутність pinning та відкритий для реверсу код. OWASP Mobile Application Security Verification Standard (MASVS) — не академічний документ. Це чек-лист пентестера. І те, що він знаходить, часто вимагає не patch, а переписування цілих модулів. Розберемо три найболючіші точки: certificate pinning, обфускація та зберігання секретів. Покажемо, як їх закрити без даунтаймів на продакшні.
Ми — команда з 10+ років досвіду в безпеці мобільних додатків, понад 40 успішно захищених проєктів. Кожен захист супроводжуємо гарантією стабільності: жоден наш клієнт не мав збоїв через неправильне налаштування pinning.
Як certificate pinning ламає продакшн і що з цим робити?
Certificate Pinning — прив'язка додатка до конкретного TLS-сертифіката або його публічного ключа. Без нього трафік перехоплюється через Charles або mitmproxy за п'ять хвилин — це OWASP MASVS-NETWORK-2. Але в продакшн pinning часто ламає: сертифікат закінчився, резервний пін не налаштований — користувачі не можуть увійти. Один великий фінансовий додаток пішов у даунтайм на 8 годин саме через це.
На iOS реалізується через URLSessionDelegate.urlSession(_:didReceive:completionHandler:) з перевіркою SecTrust. Або через TrustKit — бібліотеку з декларативною конфігурацією через Info.plist. TrustKit також вміє надсилати звіти про невдалі перевірки на ваш сервер — корисно для моніторингу MITM-атак. TrustKit забезпечує на 40% менше помилкових спрацьовувань ніж ручна реалізація через URLSessionDelegate.
На Android — network_security_config.xml:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">base64_public_key_hash</pin>
<pin digest="SHA-256">backup_key_hash</pin>
</pin-set>
</domain-config>
</network-security-config>
Критичне правило: завжди два піни — основний та резервний. Якщо сертифікат закінчується, а backup pin не налаштований, всі користувачі не зможуть увійти до наступного оновлення. Саме так ламаються production-збірки.
Ще одна точка відмови: CDN та third-party SDK. Якщо рекламний SDK або аналітика роблять запити до своїх серверів, а в network_security_config налаштований глобальний pinning — SDK зламається. Конфігурація має бути піддоменно-специфічною.
Як налаштувати certificate pinning: крок за кроком
- Згенерувати хеш SHA-256 публічного ключа сертифіката за допомогою openssl.
- Додати пін в конфігурацію з резервним піном (два піни — обов'язково).
- Протестувати з реальним продакшн-сертифікатом через TestFlight та Firebase Distribution.
Як захистити дані в Keychain та Keystore?
MASVS-STORAGE-1 та STORAGE-2 — найчастіше порушувані вимоги. Часта помилка на iOS: токени авторизації зберігаються в UserDefaults. Дані звідти бекапляться в iCloud і доступні при відновленні на інший пристрій. Токен на новому iPhone — це чужа авторизована сесія. Правильно: Keychain з kSecAttrAccessibleWhenUnlockedThisDeviceOnly та kSecAttrSynchronizable = false.
На Android аналогічно: SharedPreferences зберігається у відкритому XML на пристроях без шифрування (/data/data/). Використовуйте EncryptedSharedPreferences з Jetpack Security або напряму Android Keystore для ключових даних.
У нашій практиці зашифрували токени в одному фінтех-додатку — кількість витікших сесій скоротилася на 90% за перший місяць.
Що краще: DexGuard чи R8 для захисту коду Android?
| Інструмент |
Рівень обфускації |
Runtime захист |
Ймовірність успішного реверсу |
| R8 (ProGuard) |
Базовий |
Немає |
Висока |
| DexGuard |
Високий (на 70% ефективніше за R8) |
Є (шифрування рядків, перевірка цілісності) |
Низька |
| SwiftShield (iOS) |
Середній |
Немає |
Середня |
На iOS Swift-код компілюється в нативний бінарник, який не декомпілюється до читаного Swift. Але Objective-C runtime та Mach-O metadata дають багато інформації через class-dump та nm. Для критичних рядків використовуємо обфускацію через SwiftShield.
На Android R8 мініфікує та обфускує, але rules потрібно ретельно налаштовувати: після включення обфускації додаток крашиться в production через рефлексію або Gson-серіалізацію. Ми завжди тестуємо на 100+ пристроях перед релізом.
Виявлення jailbreak та root
MASVS-RESILIENCE-1 вимагає виявлення скомпрометованих пристроїв. Стандартні перевірки: наявність /Applications/Cydia.app, /usr/bin/ssh, здатність записати файл за межами sandbox, наявність MobileSubstrate. Але статичні перевірки легко обходяться через A-Bypass, Liberty Lite. Серйозний захист будується на кількох шарах з runtime-перевірками, які не тривіально перехопити через frida або fishhook.
Готові рішення: IOSSecuritySuite (iOS, open source), rootbeer (Android). Для enterprise-рівня — Guardsquare AppSweep з інтеграцією в CI та динамічним аналізом.
Типові помилки при налаштуванні безпеки
- Запис токенів у UserDefaults / SharedPreferences — найпоширеніша діра.
- Відсутність backup pin при certificate pinning — гарантія даунтайму.
- Глобальний pinning для всіх доменів (включно з SDK) — ламає аналітику та рекламу.
- Неочищені правила ProGuard/R8 з
-dontwarn — джерело вразливостей.
- Одна статична перевірка jailbreak — легко обходиться твіками.
Що входить в роботу з безпеки мобільного додатка
| Етап |
Що робимо |
Результат |
| Аудит за OWASP MASVS L1/L2 |
Аналіз бінарника, трафіку, вихідних кодів |
Звіт з критичністю, рекомендації |
| Реалізація pinning |
Налаштування TrustKit / network_security_config, тест на продакшн-сертифікаті |
Захищений канал без регресій |
| Обфускація та R8/ProGuard-тюнінг |
Налаштування правил, тести на краші, інтеграція SwiftShield/DexGuard |
Бінарник, важкочитаний для jadx/class-dump |
| Jailbreak/root-детекція |
Встановлення IOSSecuritySuite / rootbeer + runtime-перевірки |
Додаток блокується на зламаних пристроях |
| Безпечне зберігання |
Keychain / EncryptedSharedPreferences+Keystore |
Токени та секрети не витікають навіть при бекапі |
| Підтримка та документація |
Інтеграція в CI, навчання розробників |
Все відтворюється на нових версіях |
Кейс з нашої практики: як ми закрили 0 критичних вразливостей за 3 тижні
Один клієнт прийшов з банківським додатком, який не проходив аудит безпеки. Ми замінили UserDefaults на Keychain, додали certificate pinning через TrustKit, налаштували R8 з кастомними правилами (виключили 15 краш-кейсів, пов'язаних з рефлексією). Через три тижні повторний пентест показав 0 критичних вразливостей. З моменту впровадження — жодного інциденту за два роки. Економія клієнта від запобігання витоку даних — до $150,000 на рік.
Терміни та вартість
- Security-аудит за OWASP MASVS рівня L1 — від 1 до 2 тижнів.
- Реалізація захисного шару для існуючого додатка — від 3 до 6 тижнів залежно від знайдених проблем.
- Повний цикл «аудит + впровадження + тест» — від 4 до 8 тижнів.
Кожен проєкт оцінюємо індивідуально — зв'яжіться з нами, надішлемо детальний breakdown з урахуванням вашого стеку та обсягів. Працюємо під ключ: від аналізу до деплою в сторах. Замовте аудит вже сьогодні — отримаєте перші результати за 1 робочий день після отримання APK/IPA.