Best Strategies for App Configuration Data Protection in Financial Android Apps

Table of Contents

Protecting sensitive data in financial apps is essential to prevent security breaches that could put user information, app configurations, and financial transactions at risk. In this blog, I’ll walk you through how to secure configuration data in financial apps. We’ll begin by looking at common vulnerabilities, then move on to practical solutions like encryption and secure storage practices. Along the way, I’ll break things down step by step to help you apply these strategies with ease. Let’s dive in and make your financial app more secure!

Why Configuration Data Protection Needed?

Configuration data is essential in financial apps as it often contains API keys, URLs, and settings that control how the app behaves. In financial apps, this data is particularly sensitive. If it’s not properly secured, attackers could exploit vulnerabilities, bypass authentication, steal financial data, or even manipulate transactions.

Core Techniques to Protect Configuration Data

Here are a few core techniques to keep your configuration data secure:

  1. Using Encrypted SharedPreferences for Sensitive Data
  2. Encrypting API Keys and Tokens
  3. Using Android Keystore for Secure Key Management
  4. Network Security Configuration for Secure Data Transmission

Let’s dive into each of these, starting with Encrypted SharedPreferences.

Using Encrypted SharedPreferences for Sensitive Data

SharedPreferences is commonly used in Android to store small pieces of data, like user settings or app preferences. However, the downside is that standard SharedPreferences stores data in plain text, which can easily be accessed if the device is compromised. This is a significant security risk, especially when dealing with sensitive information like API keys.

To secure sensitive data, we can use EncryptedSharedPreferences. It encrypts the data, ensuring that even if someone gains access to the storage, they won’t be able to read the sensitive information.

Here’s how you can use EncryptedSharedPreferences:

Kotlin
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys

fun getSecureSharedPreferences(context: Context): SharedPreferences {
    val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)

    return EncryptedSharedPreferences.create(
        "secure_preferences", // Name of the preferences file
        masterKeyAlias, // The master key for encryption
        context,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )
}

fun saveConfigData(context: Context, apiKey: String) {
    val sharedPreferences = getSecureSharedPreferences(context)
    with(sharedPreferences.edit()) {
        putString("api_key", apiKey)
        apply() // Save the data securely
    }
}

fun getConfigData(context: Context): String? {
    val sharedPreferences = getSecureSharedPreferences(context)
    return sharedPreferences.getString("api_key", null) // Retrieve the secure data
}

Here,

  • MasterKeys.getOrCreate() creates a master key using AES-256 encryption. This key is used to encrypt the data.
  • EncryptedSharedPreferences.create() initializes the EncryptedSharedPreferences instance with the specified encryption schemes for both the keys and values.
  • putString() securely saves sensitive data like API keys, while getString() retrieves the encrypted value.

By using EncryptedSharedPreferences, we ensure that even if someone gains unauthorized access to the device’s storage, the data remains encrypted and safe. This is a simple yet powerful way to protect sensitive configuration data in your financial app.

Encrypting API Keys and Tokens

Hardcoding API keys and tokens directly into your app’s code can create serious security vulnerabilities. If someone decompiles your app or gains unauthorized access, these sensitive credentials could be exposed. Instead, it’s safer to store them in an encrypted format and decrypt them only when needed during runtime.

Here’s how you can use AES encryption in Kotlin to securely handle your API keys and tokens.

Kotlin
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec
import android.util.Base64

// Encrypting a string with AES
fun encryptData(plainText: String, secretKey: SecretKey): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey)
    val iv = cipher.iv
    val encryptedData = cipher.doFinal(plainText.toByteArray())
    val ivAndEncryptedData = iv + encryptedData
    return Base64.encodeToString(ivAndEncryptedData, Base64.DEFAULT)
}

// Decrypting the encrypted string
fun decryptData(encryptedText: String, secretKey: SecretKey): String {
    val ivAndEncryptedData = Base64.decode(encryptedText, Base64.DEFAULT)
    val iv = ivAndEncryptedData.sliceArray(0 until 12) // Extract the 12-byte IV
    val encryptedData = ivAndEncryptedData.sliceArray(12 until ivAndEncryptedData.size)
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    val gcmParameterSpec = GCMParameterSpec(128, iv) // 128-bit authentication tag length
    cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec)
    val decryptedData = cipher.doFinal(encryptedData)
    return String(decryptedData)
}

// Generate Secret Key for AES
fun generateSecretKey(): SecretKey {
    val keyGenerator = KeyGenerator.getInstance("AES")
    keyGenerator.init(256) // AES 256-bit encryption
    return keyGenerator.generateKey()
}
  • AES/GCM/NoPadding: This mode provides strong encryption and also ensures no unnecessary padding is added, keeping the data size as small as possible.
  • Initialization Vector (IV): The IV is crucial for ensuring that even if the same data is encrypted multiple times, the output will differ. It’s stored alongside the encrypted data and is required for decryption.
  • generateSecretKey(): This method creates a 256-bit AES key, which can be used for both encryption and decryption. To further enhance security, you can store this key in the Android Keystore.

By using AES encryption to handle your API keys and tokens, you’re adding an extra layer of security to prevent unauthorized access to your financial app’s sensitive data. This approach ensures that your sensitive information remains secure, even if the device is compromised.

Btw, I know you might be wondering about one term. Any guesses..? Without further delay, let’s take a look!

What is an IV (Initialization Vector)?

An Initialization Vector (IV) is a random value used in cryptographic algorithms, such as AES, to ensure that each encryption operation produces unique results — even when encrypting the same data multiple times with the same key.

Why is IV Important?

  • Prevents Repeated Patterns:
    Without an IV, encrypting the same data with the same key would always result in the same ciphertext. This predictability is a security risk. The IV ensures that even when encrypting identical data, the output (ciphertext) will be different each time, making it harder for attackers to detect patterns or deduce information.
  • Enhances Security:
    In encryption modes like AES-CBC (Cipher Block Chaining) or AES-GCM (Galois/Counter Mode), the IV plays a crucial role by adding randomness to the encryption process. This added randomness strengthens the encryption, making it more resistant to attacks.
  • Must Be Unique:
    The IV doesn’t need to be kept secret, but it must be unique for each encryption operation. Reusing the same IV with the same key for different data introduces vulnerabilities. When an IV is reused, attackers may be able to spot patterns or exploit weaknesses in the encryption.

How Does the IV Work?

  • During Encryption:
    The IV is combined with the plaintext and encryption key to create the ciphertext. Its role is to introduce randomness, ensuring that even identical plaintexts will produce different ciphertexts when encrypted.
  • During Decryption:
    To decrypt the data properly, the same IV used during encryption must be provided. It’s typically sent alongside the ciphertext, ensuring the receiver can use it during decryption to recover the original data.

Storing and Transmitting the IV

The IV itself doesn’t need to be kept secret, but it must be made available to the receiver. Usually, it’s transmitted along with the encrypted data, either as a prefix or in a predefined format. This ensures that the IV can be used during decryption. However, even though the IV isn’t secret, it still must be securely transmitted to ensure proper decryption.

Let’s get back to our discussion on App Configuration Data Protection and see how we can use Android Keystore for secure key management.

Using Android Keystore for Secure Key Management

Storing encryption keys directly in the app can leave them vulnerable to attacks. To avoid this, we can use the Android Keystore system, which securely stores keys either in hardware or a secure enclave, ensuring that only the app has access to them. This adds a significant layer of protection, especially for sensitive data.

Here’s how you can generate and securely manage keys using the Keystore:

Kotlin
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey

// Generate and store a key in Android Keystore
fun createKey() {
    val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
    val keyGenParameterSpec = KeyGenParameterSpec.Builder(
        "SecureKeyAlias",
        KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
    ).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
     .build()
    keyGenerator.init(keyGenParameterSpec)
    keyGenerator.generateKey()
}

// Retrieve the secret key from Keystore
fun getSecretKey(): SecretKey? {
    val keyStore = KeyStore.getInstance("AndroidKeyStore")
    keyStore.load(null)
    return keyStore.getKey("SecureKeyAlias", null) as SecretKey?
}
  • KeyGenParameterSpec.Builder: This part sets the encryption requirements, such as the encryption block mode and padding. In this case, we’re using AES with GCM mode, which is both secure and efficient.
  • createKey(): This function creates a new AES encryption key and securely stores it in the Keystore with the alias SecureKeyAlias. The key is only accessible to the app, making it safe from potential leaks.
  • getSecretKey(): This function retrieves the stored key from the Keystore when needed for encryption or decryption. The key is never exposed in the code, adding an extra layer of security.

By using the Android Keystore, we avoid the risk of exposing sensitive keys within the app, ensuring a higher level of security for encryption operations.

Network Security Configuration for Secure Data Transmission

In financial apps, securely transmitting sensitive data over HTTPS is critical to prevent man-in-the-middle attacks. If not properly configured, cleartext traffic (HTTP) can expose this data to unauthorized access. To ensure your app uses HTTPS and blocks any unencrypted traffic, you can define network security policies using a configuration file.

Here’s how to enforce HTTPS using a network security configuration in your app

Create a network security configuration file (network_security_config.xml) in the res/xml folder:

XML
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">yourapi.com</domain>
    </domain-config>
</network-security-config>

    Link the configuration in your AndroidManifest.xml:

    XML
    <application
        android:networkSecurityConfig="@xml/network_security_config"
        ... >
    </application>
    
    • cleartextTrafficPermitted="false": This setting ensures that the app only allows encrypted HTTPS traffic and blocks any HTTP (cleartext) traffic, preventing sensitive data from being exposed.
    • <domain> tag: You specify trusted domains (like yourapi.com) that the security settings apply to, including all of its subdomains (by setting includeSubdomains="true").

    By adding this configuration, you’re ensuring that your financial app’s data transmissions remain secure, guarding against potential security threats.

    Conclusion 

    Securing app configuration data in financial apps is essential for protecting sensitive user information and maintaining trust. By implementing practices like using EncryptedSharedPreferences, encrypting sensitive values, storing keys in the Android Keystore, and enforcing HTTPS, you can significantly reduce the risk of data breaches and vulnerabilities.

    These steps will help ensure that your financial Android app handles sensitive data securely, giving users the peace of mind they need when using your app.

    Skill Up: Software & AI Updates!

    Receive our latest insights and updates directly to your inbox

    Related Posts

    error: Content is protected !!