Перейти к содержанию

Доступ к ключам из AndroidKeyStore без требования аутентификации

Описание

По умолчанию ключи, созданные в Android KeyStore, можно использовать без подтверждения личности пользователя, если при их генерации не задан флаг setUserAuthenticationRequired(true) в KeyGenParameterSpec.Builder.

В этом случае любое запущенное от имени приложения (или вредоносный код на рут-устройстве) может подписывать или расшифровывать данные, не показывая PIN/биометрию. Документация Google подчёркивает, что для защиты конфиденциальных операций следует явно требовать аутентификацию для каждого обращения к ключу или в пределах заданного окна времени.

Потенциальные последствия

  • Неавторизованное использование ключа злоумышленником, получившим доступ к процессу приложения (root, эксплуатация уязвимости, код-инжекция).
  • Обход биометрической/пин-защиты и, как следствие, расшифровка защищённых баз, токенов или сетевого трафика.
  • Несоответствие требованиям OWASP MASVS (M9) и рекомендациям Google Play о защите пользовательских данных.
  • Невыполнение отраслевых регуляций (PSD2 /SCA, PCI DSS), требующих пользовательского подтверждения при крипто-операциях, связанных с финансовыми транзакциями.

Рекомендации

  1. Всегда требуйте аутентификацию при генерации ключа

    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.

  2. Используйте Biometric Prompt / Device Credential

    Отображайте системный диалог (BiometricPrompt) перед каждой операцией или в заданном интервале (например, 30 сек). (Android Developers)

  3. Для ключей на устройствах с Android 9 + требуйте StrongBox/TEE

    Совместите флаги setIsStrongBoxBacked(true) и setUserAuthenticationRequired(true); это минимизирует риск извлечения ключа даже на рут-устройствах.

  4. Проверяйте аттестацию и свойство isUserAuthenticationRequired()

    После загрузки ключа через KeyInfo убедитесь, что флаг действительно установлен, иначе выбросьте исключение и запретите операцию.

Дополнительные рекомендации

  • Для сильно чувствительных данных используйте setUserConfirmationRequired(true) (API 30+) — это требует explicit user confirmation после Biometric Prompt.
  • Следите за ошибками KeyPermanentlyInvalidatedException — ключи, требующие аутентификацию, могут стать недействительными после изменения отпечатков пальцев или PIN.

Ссылки

  1. KeyGenParameterSpec.Builder — setUserAuthenticationRequired (Android Developers)
  2. Android Keystore system («User authentication for key use») (Android Developers)
  3. WithSecure Labs — «How Secure is your Android Keystore Authentication?» (Withsecure Labs)
  4. Biometric Prompt — Developer Guide (Android Developers)
  5. Stack Overflow — Пример проверки isInsideSecureHardware() и isUserAuthenticationRequired (Android Developers)
К началу