-- REVIEW -- The use of a hard-coded or world-readable cryptographic key significantly increases the possibility that encrypted data may be recovered. Once it is obtained by an attacker, the task to decrypt the sensitive data becomes trivial, and the initial idea to protect confidentiality fails.
When using symmetric cryptography, the key needs to be stored within the device and it is just a matter of time and effort from the attacker to identify it.
Consider the following scenario: An application is reading and writing to an encrypted database but the decryption is done based on a hardcoded key:
this.db = localUserSecretStore.getWritableDatabase("SuperPassword123");
Since the key is the same for all App installations it is trivial to obtain it. The advantages of having sensitive data encrypted are gone, and there is effectively no benefit in using encryption in this way. Similarly, look for hardcoded API keys / private keys and other valuable pieces. Encoded/encrypted keys is just another attempt to make it harder but not impossible to get the crown jewels.
Let's consider this piece of code:
//A more complicated effort to store the XOR'ed halves of a key (instead of the key itself)
private static final String[] myCompositeKey = new String[]{
"oNQavjbaNNSgEqoCkT9Em4imeQQ=","3o8eFOX4ri/F8fgHgiy/BS47"
};
Algorithm to decode the original key in this case might look like this[1]:
public void useXorStringHiding(String myHiddenMessage) {
byte[] xorParts0 = Base64.decode(myCompositeKey[0],0);
byte[] xorParts1 = Base64.decode(myCompositeKey[1],0);
byte[] xorKey = new byte[xorParts0.length];
for(int i = 0; i < xorParts1.length; i++){
xorKey[i] = (byte) (xorParts0[i] ^ xorParts1[i]);
}
HidingUtil.doHiding(myHiddenMessage.getBytes(), xorKey, false);
}
Verify common places where secrets are usually hidden:
- resources (typically at res/values/strings.xml)
Example:
<resources>
<string name="app_name">SuperApp</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="secret_key">My_S3cr3t_K3Y</string>
</resources>
- build configs, such as in local.properties or gradle.properties
Example:
buildTypes {
debug {
minifyEnabled true
buildConfigField "String", "hiddenPassword", "\"${hiddenPassword}\""
}
}
- shared preferences, typically at /data/data/package_name/shared_prefs
If you need to store a key for repeated use, use a mechanism, such as KeyStore[2], that provides a mechanism for long term storage and retrieval of cryptographic keys.
- M6 - Broken Cryptography
- V3.1: "The app does not rely on symmetric cryptography with hardcoded keys as a sole method of encryption"
- V3.5: "The app doesn't re-use the same cryptographic key for multiple purposes"
- CWE-320: Key Management Errors
- CWE-321: Use of Hard-coded Cryptographic Key
[1] Hiding Passwords in Android - https://github.com/pillfill/hiding-passwords-android/ [2] KeyStore - https://developer.android.com/reference/java/security/KeyStore.html [3] Hiding Secrets in Android - https://rammic.github.io/2015/07/28/hiding-secrets-in-android-apps/ [4] Securely storing secrets in Android - https://medium.com/@ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3#.7z5yruotu
-- REVIEW -- Choosing good cryptographic algorithm alone is not enough. Often security of otherwise sound algorithms can be affected if misconfigured. Many previously strong algorithms and their configurations are now considered vulnerable or non-compliant with best practices. It is therefore important to periodically check current best practices and adjust configurations accordingly.
-- TODO [Describe Static Analysis on Verifying the Configuration of Cryptographic Standard Algorithms : how to assess this given either the source code or installer package (APK/IPA/etc.), but without running the app. Tailor this to the general situation (e.g., in some situations, having the decompiled classes is just as good as having the original source, in others it might make a bigger difference). If required, include a subsection about how to test with or without the original sources.] --
-- TODO [Clarify the purpose of "Use the <sup> tag to reference external sources, e.g. Meyer's recipe for tomato soup[1]."] --
-- TODO [Develop Static Analysis with source code of "Verifying the Configuration of Cryptographic Standard Algorithms"] --
-- TODO [Describe how to test for this issue "Verifying the Configuration of Cryptographic Standard Algorithms" by running and interacting with the app. This can include everything from simply monitoring network traffic or aspects of the app’s behavior to code injection, debugging, instrumentation, etc.] --
-- REVIEW -- Use cryptographic algorithm configurations that are currently considered strong, such those from NIST1 and BSI2 recommendations.
- M6 - Broken Cryptography
-- REVIEW --
- V3.3: "The app uses cryptographic primitives that are appropriate for the particular use-case, configured with parameters that adhere to industry best practices"
-- REVIEW --
- CWE-326: Inadequate Encryption Strength
-- REVIEW --
- [1] NIST recommendations (2016) - https://www.keylength.com/en/4/
- [2] BSI recommendations (2017) - https://www.keylength.com/en/8/
-- TODO [Add relevant tools for "Verifying the Configuration of Cryptographic Standard Algorithms"] --
- Enjarify - https://github.com/google/enjarify
When software generates predictable values in a context requiring unpredictability, it may be possible for an attacker to guess the next value that will be generated, and use this guess to impersonate another user or access sensitive information.
Identify all the instances of random number generators and look for either custom or known insecure java.util.Random class. This class produces an identical sequence of numbers for each given seed value; consequently, the sequence of numbers is predictable. The following sample source code shows weak random number generation:
import java.util.Random;
// ...
Random number = new Random(123L);
//...
for (int i = 0; i < 20; i++) {
// Generate another random integer in the range [0, 20]
int n = number.nextInt(21);
System.out.println(n);
}
Once an attacker is knowing what type of weak pseudo-random number generator (PRNG) is used, it can be trivial to write proof-of-concept to generate the next random value based on previously observed ones, as it was done for Java Random[1]. In case of very weak custom random generators it may be possible to observe the pattern statistically. Although the recommended approach would anyway be to decompile the APK and inspect the algorithm (see Static Analysis).
Use a well-vetted algorithm that is currently considered to be strong by experts in the field, and select well-tested implementations with adequate length seeds. Prefer the no-argument constructor of SecureRandom that uses the system-specified seed value to generate a 128-byte-long random number[2]. In general, if a PRNG is not advertised as being cryptographically secure (e.g. java.util.Random), then it is probably a statistical PRNG and should not be used in security-sensitive contexts. Pseudo-random number generators can produce predictable numbers if the generator is known and the seed can be guessed[3]. A 128-bit seed is a good starting point for producing a "random enough" number.
The following sample source code shows the generation of a secure random number:
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
// ...
public static void main (String args[]) {
SecureRandom number = new SecureRandom();
// Generate 20 integers 0..20
for (int i = 0; i < 20; i++) {
System.out.println(number.nextInt(21));
}
}
- V3.6: "All random values are generated using a sufficiently secure random number generator"
- M6 - Broken Cryptography
- CWE-330: Use of Insufficiently Random Values
[1] Predicting the next Math.random() in Java - http://franklinta.com/2014/08/31/predicting-the-next-math-random-in-java/ [2] Generation of Strong Random Numbers - https://www.securecoding.cert.org/confluence/display/java/MSC02-J.+Generate+strong+random+numbers [3] Proper seeding of SecureRandom - https://www.securecoding.cert.org/confluence/display/java/MSC63-J.+Ensure+that+SecureRandom+is+properly+seeded