Доступ к ключам из AndroidKeyStore без требования аутентификации
Описание
По умолчанию ключи, созданные в Android KeyStore, можно использовать без подтверждения личности пользователя, если при их генерации не задан флаг setUserAuthenticationRequired(true)
в KeyGenParameterSpec.Builder
.
В этом случае любое запущенное от имени приложения (или вредоносный код на рут-устройстве) может подписывать или расшифровывать данные, не показывая PIN/биометрию. Документация Google подчёркивает, что для защиты конфиденциальных операций следует явно требовать аутентификацию для каждого обращения к ключу или в пределах заданного окна времени.
Потенциальные последствия
- Неавторизованное использование ключа злоумышленником, получившим доступ к процессу приложения (root, эксплуатация уязвимости, код-инжекция).
- Обход биометрической/пин-защиты и, как следствие, расшифровка защищённых баз, токенов или сетевого трафика.
- Несоответствие требованиям OWASP MASVS (M9) и рекомендациям Google Play о защите пользовательских данных.
- Невыполнение отраслевых регуляций (PSD2 /SCA, PCI DSS), требующих пользовательского подтверждения при крипто-операциях, связанных с финансовыми транзакциями.
Рекомендации
-
Всегда требуйте аутентификацию при генерации ключа
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) // <-- критично .setUserAuthenticationValidityDurationSeconds(0) // 0 = каждый раз .build();
Для операций подписи/шифрования пользователь будет запрашиваться биометрией или 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)