Insecure local authentication¶
ID: java/android/insecure-local-authentication
Kind: problem
Security severity: 4.4
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-287
Query suites:
- java-code-scanning.qls
- java-security-extended.qls
- java-security-and-quality.qls
Click to see the query in the CodeQL repository
Biometric local authentication such as fingerprint recognition can be used to protect sensitive data or actions within an application. However, if this authentication does not use a KeyStore
-backed key, it can be bypassed by a privileged malicious application, or by an attacker with physical access using application hooking tools such as Frida.
Recommendation¶
Generate a secure key in the Android KeyStore
. Ensure that the onAuthenticationSuccess
callback for a biometric prompt uses it in a way that is required for the sensitive parts of the application to function, such as by using it to decrypt sensitive data or credentials.
Example¶
In the following (bad) case, no CryptoObject
is required for the biometric prompt to grant access, so it can be bypassed.
biometricPrompt.authenticate(
cancellationSignal,
executor,
new BiometricPrompt.AuthenticationCallback {
@Override
// BAD: This authentication callback does not make use of a `CryptoObject` from the `result`.
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
grantAccess()
}
}
)
In the following (good) case, a secret key is generated in the Android KeyStore
. The application requires this secret key for access, using it to decrypt data.
private void generateSecretKey() {
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
"MySecretKey",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build();
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(keyGenParameterSpec);
keyGenerator.generateKey();
}
private SecretKey getSecretKey() {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
return ((SecretKey)keyStore.getKey("MySecretKey", null));
}
private Cipher getCipher() {
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
}
public prompt(byte[] encryptedData) {
Cipher cipher = getCipher();
SecretKey secretKey = getSecretKey();
cipher.init(Cipher.DECRYPT_MODE, secretKey);
biometricPrompt.authenticate(
new BiometricPrompt.CryptoObject(cipher),
cancellationSignal,
executor,
new BiometricPrompt.AuthenticationCallback() {
@Override
// GOOD: This authentication callback uses the result to decrypt some data.
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
Cipher cipher = result.getCryptoObject().getCipher();
byte[] decryptedData = cipher.doFinal(encryptedData);
grantAccessWithData(decryptedData);
}
}
);
}
References¶
OWASP Mobile Application Security: Android Local Authentication
OWASP Mobile Application Security: Testing Biometric Authentication
WithSecure: How Secure is your Android Keystore Authentication?
Android Developers: Biometric Authentication
Common Weakness Enumeration: CWE-287.