Доступ к ключам из AndroidKeyStore без требования аутентификации
![]() |
Критичность: НИЗКИЙ |
| Способ обнаружения: DAST |
Описание
По умолчанию ключи, созданные в Android KeyStore, можно использовать без подтверждения личности пользователя, если при их генерации не задан флаг setUserAuthenticationRequired(true) в KeyGenParameterSpec.Builder.
В этом случае любое запущенное от имени приложения (или вредоносный код на рут-устройстве) может подписывать или расшифровывать данные, не показывая PIN/биометрию. Документация Google подчёркивает, что для защиты конфиденциальных операций следует явно требовать аутентификацию для каждого обращения к ключу или в пределах заданного окна времени.
Потенциальные последствия
- Неавторизованное использование ключа злоумышленником, получившим доступ к процессу приложения (root, эксплуатация уязвимости, код-инжекция).
- Обход биометрической/пин-защиты и, как следствие, расшифровка защищённых баз, токенов или сетевого трафика.
- Несоответствие требованиям OWASP MASVS (M9) и рекомендациям Google Play о защите пользовательских данных.
- Невыполнение отраслевых регуляций (PSD2 /SCA, PCI DSS), требующих пользовательского подтверждения при крипто-операциях, связанных с финансовыми транзакциями.
Рекомендации
-
Всегда требуйте аутентификацию при генерации ключа
На API 30+ используйте
setUserAuthenticationParameters(timeout, type). Значениеtimeout = 0означает, что каждая операция с ключом должна быть авторизована отдельной аутентификацией пользователя:KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) // <-- критично // API 30+: timeout = 0 — аутентификация перед каждой операцией; // type — допустимые способы подтверждения личности .setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG | KeyProperties.AUTH_DEVICE_CREDENTIAL) .build();Для совместимости со старыми устройствами (API < 30) используйте устаревший метод
setUserAuthenticationValidityDurationSeconds(-1). Именно значение-1требует аутентификации при каждом обращении к ключу (положительное значение задаёт окно в секундах, в течение которого повторная аутентификация не нужна):// Устаревший метод (deprecated с API 30), только для API < 30: // -1 — аутентификация перед каждой операцией builder.setUserAuthenticationValidityDurationSeconds(-1);Для операций подписи/шифрования пользователь будет запрашиваться биометрией или PIN.
-
Используйте Biometric Prompt / Device Credential
Отображайте системный диалог (
BiometricPrompt) перед каждой операцией или в заданном интервале (например, 30 сек). (Android Developers) -
Для ключей на устройствах с Android 9 + требуйте StrongBox/TEE
Совместите флаги
setIsStrongBoxBacked(true)иsetUserAuthenticationRequired(true); это минимизирует риск извлечения ключа даже на рут-устройствах. -
Проверяйте аттестацию и свойство
isUserAuthenticationRequired()После загрузки ключа через
KeyInfoубедитесь, что флаг действительно установлен, иначе выбросьте исключение и запретите операцию.
Дополнительные рекомендации
- Для сильно чувствительных данных используйте
setUserConfirmationRequired(true)(API 30+) — это требует explicit user confirmation после Biometric Prompt. - Следите за ошибками
KeyPermanentlyInvalidatedException— ключи, требующие аутентификацию, могут стать недействительными после изменения отпечатков пальцев или PIN.
Ссылки
- KeyGenParameterSpec.Builder —
setUserAuthenticationRequired(Android Developers) - Android Keystore system («User authentication for key use») (Android Developers)
- WithSecure Labs — «How Secure is your Android Keystore Authentication?» (Withsecure Labs)
- Biometric Prompt — Developer Guide (Android Developers)
- Stack Overflow — Пример проверки
isInsideSecureHardware()иisUserAuthenticationRequired(Android Developers)
