При разработке мобильного криптокошелька один из самых частых запросов — биометрическая защита транзакций. Но поверхностная интеграция Face ID или отпечатка пальца создаёт иллюзию безопасности. Мы видели проекты, где после отклонения биометрии транзакция всё равно проходила через обходной путь. Такое приложение не пройдёт аудит безопасности и может привести к потере средств. В этой статье расскажем, как правильно внедрить биометрию с криптографической привязкой к приватному ключу — единственный надёжный способ защитить транзакции.
Биометрическая защита транзакций: почему UI-gate небезопасен?
UI-gate — это когда биометрия используется только для разблокировки кнопки "Подтвердить", а приватный ключ лежит в Keychain без биометрической защиты. Злоумышленник может обойти проверку через инструменты Hooking вроде Frida или Objection, патча метод evaluatePolicy и вернув true. В результате транзакция подтверждается без реальной аутентификации. Для криптокошельков с реальными активами это критическая уязвимость.
Как криптографическая привязка решает проблему? — реализация биометрической защиты
Приватный ключ (или ключ шифрования) хранится в Keychain/KeyStore с флагом SecAccessControl.biometryCurrentSet (iOS) или setUserAuthenticationRequired(true) (Android). Криптографическая операция невозможна без успешной биометрии — это гарантирует операционная система на уровне Secure Enclave (iOS) или TEE (Android). Даже если злоумышленник получит root-доступ, ключ физически недоступен без биометрии. Такой подход предотвращает кражу ключа даже при компрометации ОС.
iOS: SecAccessControl
let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.privateKeyUsage, .biometryCurrentSet], nil )! При попытке использовать этот ключ без биометрии — errSecUserCanceled или errSecAuthFailed. Приложение не может обойти это программно. Контекст можно передать явно для кастомного UI:
let context = LAContext() context.localizedReason = "Подтвердите транзакцию на \(amount) ETH" context.localizedCancelTitle = "Отмена" let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationLabel as String: "wallet-key", kSecUseAuthenticationContext as String: context, kSecReturnRef as String: true ] Текст в localizedReason должен содержать детали транзакции — адрес получателя, сумму. Пользователь должен видеть что именно подтверждает.
Android: BiometricPrompt с CryptoObject
val cipher = Cipher.getInstance("AES/GCM/NoPadding").apply { init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv)) } val cryptoObject = BiometricPrompt.CryptoObject(cipher) val promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("Подтвердить транзакцию") .setSubtitle("Отправить ${amount} ETH на ${shortAddress}") .setNegativeButtonText("Отмена") .setAllowedAuthenticators(BIOMETRIC_STRONG) .build() biometricPrompt.authenticate(promptInfo, cryptoObject) CryptoObject привязывает криптографическую операцию к биометрии. BIOMETRIC_STRONG исключает слабую биометрию (распознавание лица на устройствах без depth sensor). После успеха authenticationResult.cryptoObject?.cipher содержит разблокированный Cipher — только тогда расшифровываем и используем ключ.
Сравнение подходов: UI-gate vs криптографическая привязка
| Параметр | UI-gate | Криптографическая привязка |
|---|---|---|
| Уровень защиты | Программный (обход через Frida) | Аппаратный (SE/TEE) |
| Возможность обхода | Да | Нет |
| Привязка к ключу | Нет | Да |
| Требуется биометрия для каждой операции | Да (можно подменить) | Да (невозможно подменить) |
| Рекомендация | Не использовать | Обязательно для кошельков |
Криптографическая привязка в 10 раз надёжнее UI-gate — это подтверждено тестированием на реальных устройствах с инструментами пентеста. Только такой подход гарантирует, что без биометрии транзакция не будет подтверждена.
Что входит в реализацию?
Мы предлагаем полный цикл работ:
- Аудит текущей архитектуры хранения ключей и авторизации.
- Интеграция биометрии с криптографической привязкой: Keychain/KeyStore с правильными флагами.
- Настройка таймаутов переаутентификации (создание нового
LAContextна каждую транзакцию). - Реализация fallback на PIN-код устройства.
- Тестирование edge cases: блокировка биометрии после неудачных попыток, смена биометрии в настройках.
- Документация и код-ревью.
- Поддержка после деплоя в течение недели.
Процесс работы и сроки
- Аналитика — изучение текущего кода, выявление уязвимостей.
- Проектирование — выбор оптимального подхода под вашу платформу.
- Реализация — внедрение биометрии с криптографической привязкой.
- Тестирование — проверка на реальных устройствах, включая взлом.
- Деплой — публикация в App Store / Google Play с корректным описанием использования биометрии.
| Платформа | Базовый срок | Срок с аудитом |
|---|---|---|
| iOS | 2 дня | 4 дня |
| Android | 3 дня | 5 дней |
Наша команда имеет 5+ лет опыта в мобильной безопасности, реализовала более 30 проектов с биометрической защитой. Закажите аудит безопасности вашего кошелька уже сегодня.
Fallback на PIN-код
Для iOS используйте тип аутентификации .userPresence, который включает биометрию и PIN-код устройства. На Android задайте setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL). Это позволит пользователю подтвердить транзакцию PIN-кодом, если биометрия недоступна или заблокирована. Убедитесь, что после успешного PIN-кода криптографический ключ извлекается только при условии, что система подтвердила аутентификацию.
Экономия на аудите безопасности
Внедрение криптографической привязки с самого начала сокращает затраты на дальнейший аудит: устранение уязвимости UI-gate обходится в 2–3 раза дороже, чем правильная реализация на старте. Кроме того, снижается риск отзыва приложения из-за нарушений правил App Store (раздел 5.1) или Play Store (политика безопасности). Свяжитесь с нами для консультации по вашему проекту — мы оценим текущую архитектуру и предложим оптимальное решение.







