Add documentation

This commit is contained in:
Joe Farebrother
2024-02-02 14:34:43 +00:00
parent 88c2ccbecf
commit 2a00375bb7
3 changed files with 101 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Biometric local authentication such as fingerprint recognistion can be used to protect sensitive data or actions within an application.
However, if this authentication does not make use of a <code>KeyStore</code>-backed key, it is able to be bypassed by a privileged malicious application or an attacker with physical access.
</p>
</overview>
<recommendation>
<p>
Generate a secure key in the Android <code>KeyStore</code> and ensure that the <code>onAuthenticaionSuccess</code> 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.
</p>
</recommendation>
<example>
<p>In the following (bad) case, no <code>CryptoObject</code> is required for the biometric prompt to grant access, so it can be bypassed.</p>
<sample src="AndroidInsecureLocalAuthenticationBad.java" />
<p>In he following (good) case, a secret key is generated in the Android <code>KeyStore</code> that is required for the application to grant access.</p>
<sample src="AndroidInsecureLocalAuthenticationGood.java" />
</example>
<references>
<li>
OWASP Mobile Application Security: <a href="https://mas.owasp.org/MASTG/Android/0x05f-Testing-Local-Authentication/">Android Local Authentication</a>
</li>
<li>
OWASP Mobile Application Security: <a href="https://mas.owasp.org/MASTG/tests/android/MASVS-AUTH/MASTG-TEST-0018/">Testing Biometric Authentication</a>
</li>
<li>
WithSecure: <a href="https://labs.withsecure.com/publications/how-secure-is-your-android-keystore-authentication">How Secure is your Android Keystore Authentication?</a>
</li>
<li>
Android Developers: <a href="https://developer.android.com/training/sign-in/biometric-auth">Biometric Authentication</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,11 @@
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()
}
}
)

View File

@@ -0,0 +1,48 @@
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() {
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);
}
}
);
}