When storing local data in a mobile application, it is common to use a database that can be encrypted. When encryption of this database is enabled, the encryption key must be protected properly.
Mobile applications often need to store data (which might be sensitive) locally. For Android, there exist several libraries that simplify this process by offering a feature-rich database system. SQLCipher and Realm are examples of such libraries. These libraries often add support for database encryption, to protect the contents from being read by other apps or by attackers.
When using encryption for such a database, it is important that the encryption key stays secret. If this key is hardcoded in the application, then it should be considered compromised. The key will be known by anyone with access to the application’s binary code or source code. This means that the sensitive encrypted data can be decrypted by anyone having access to the binary of the mobile application.
Furthermore, if the key is hardcoded, it is the same for every user. A compromise of this encryption key implicates every user of the app.
The encryption key is meant to stay secret and should not be hard-coded in the application as it would mean that:
If an attacker is able to find the encryption key for the mobile database, this can potentially have severe consequences.
If a mobile database is encrypted, it is likely to contain data that is sensitive for the user or the app publisher. For example, it can contain personally identifiable information (PII), financial data, login credentials, or other sensitive user data.
By not protecting the encryption key properly, it becomes very easy for an attacker to recover it and then decrypt the mobile database. At that point, the theft of sensitive data might lead to identity theft, financial fraud, and other forms of malicious activities.
In the example below, a local database is opened using a hardcoded key. To fix this, the key is moved to a secure location instead and retrieved
using a getKey() method.
String key = "gb09ym9ydoolp3w886d0tciczj6ve9kszqd65u7d126040gwy86xqimjpuuc788g";
RealmConfiguration config = new RealmConfiguration.Builder();
.encryptionKey(key.toByteArray()) // Noncompliant
.build();
Realm realm = Realm.getInstance(config);
RealmConfiguration config = new RealmConfiguration.Builder()
.encryptionKey(getKey())
.build();
Realm realm = Realm.getInstance(config);
The Android Keystore system allows apps to store encryption keys in a container that is protected on a system level. Additionally, it can restrict when and how the keys are used. For example, it allows the app to require user authentication (for example using a fingerprint) before the key is made available. This is the recommended way to store cryptographic keys on Android.
As user devices are less trusted than controlled environments such as the application backend, the latter should be preferred for the storage of encryption keys. This requires that a user’s device has an internet connection, which may not be suitable for every use case.
In general, it is always preferable to store as little sensitive data on user devices as possible.
Of course, some sensitive data always has to be stored on client devices, such as the data required for authentication. In this case, consider whether the application logic can also function with a hash (or otherwise non-reversible form) of that data. For example, if an email address is required for authentication, it might be possible to use and store a hashed version of this address instead.
In the example below, a local database is opened using a hardcoded key. To fix this, the key is moved to a secure location instead and retrieved
using a getKey() method.
String key = "gb09ym9ydoolp3w886d0tciczj6ve9kszqd65u7d126040gwy86xqimjpuuc788g";
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase("test.db", key, null); // Noncompliant
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase("test.db", getKey(), null);
The Android Keystore system allows apps to store encryption keys in a container that is protected on a system level. Additionally, it can restrict when and how the keys are used. For example, it allows the app to require user authentication (for example using a fingerprint) before the key is made available. This is the recommended way to store cryptographic keys on Android.
As user devices are less trusted than controlled environments such as the application backend, the latter should be preferred for the storage of encryption keys. This requires that a user’s device has an internet connection, which may not be suitable for every use case.
In general, it is always preferable to store as little sensitive data on user devices as possible.
Of course, some sensitive data always has to be stored on client devices, such as the data required for authentication. In this case, consider whether the application logic can also function with a hash (or otherwise non-reversible form) of that data. For example, if an email address is required for authentication, it might be possible to use and store a hashed version of this address instead.