Storing sensitive information in a private file inside the application's directory

  INFO  
Detection method   DAST         FILES  

Description

An application stores sensitive information in a private file inside the application's directory.

To understand exactly what needs to be protected, you have to analyse what kind of data is being handled and stored by the application and what part of that data is confidential. It is customary in such cases to rely upon legislation and common sense. There is no use in encrypting all information stored by an application, moreover, this affects operating speed and operational stability. Instead, you need to clearly define what is considered as confidential data by your application or by your company, and focus your attention on that data.

It is generally assumed that as few confidential information as possible should be stored in a local storage (both internal and external). In most cases, however, there is an unavoidable need to store such information. Because, for example, from the usability point of view, you shouldn't force a user to enter a complex password by every launch of an application. Most applications have to put some authentication token into a local cache. Personally identifiable information (PII) and other types of confidential data could also be retained if a specific scenario calls for it.

An application can store data in several places, for example, on a device or on an external SD-card. The most common ways to store data on a device are:

  • Shared Preferences
  • SQLite Databases
  • Realm databases
  • Internal memory
  • External storage

Note, that no approach mentioned above can guarantee security of the stored data. If the data is not encoded or hashed before it is put into storage, an attacker can find a way to access that data.

Note! There is a common (and erroneous) belief that the data stored in the internal application's directory is already protected by a sandbox mechanism and there is no possibility for a malicious person to access that data. Truth is, there are many possibilities: from the simple local or cloud backup of the application, to a physical access to the device and exploitation of various vulnerabilities.

Plain text information put into the application's directory is not protected!

Putting information into an external storage or shared directories makes the information accessible to everybody. It's no good to store confidential information in such files.

Recommendations

Selection of a method to store the confidential information depends on the type of the data. If there is a need to store encryption keys—the best choice would be the system storage, AndroidKeyStore. Unfortunately, this is not always possible, so you have to choose a method that provides the maximum possible protection taking into account the Android version:

  • On Android API<18 keys must be stored inside the application's directory in BKS.
  • On Android API>=18 RSA keys must be stored in AndroidKeyStore, and AES keys—in BKS.
  • On Android API>=23 RSA and AES keys must be stored in AndroidKeyStore.

Also, don't forget that when using the BKS in the internal application's directory, an additional strong password is required to protect the keystore and the keys within it. A good idea would be to check the generated password against a database for most common passwords and ensure that the password meets minimum requirements:

  • The password is at least 20 characters in length
  • At least one lower-case letter is required
  • At least one upper-case letter is required
  • At least one numeric character is required
  • At least one special character is required

Android is equipped with a variety of cryptographic functions that help ensuring confidentiality and integrity of data. There are methods that enable Android applications to securely encrypt and decrypt data (maintaining confidentiality), to authenticate messages (MAC) and digital signatures (ensuring integrity).

The below charts can help you choosing the most applicable encryption method and type of key:

 

 

Password-based encryption/decryption

Let us take a closer look at encryption/decryption method based on a user password. In this case there is no need to store the encryption key as it is created on the fly using the password provided by a user:

Rules:

1. Specify the encryption mode and padding explicitly.

2. Use intractable cryptographic technologies that include an algorithm, a block cipher mode and padding.

3. Use salt when generating keys from passwords.

4. Use an appropriate hash iteration count when generating keys from passwords.

5. Use the key length that ensures strong encryption.

package com.mobix.android.cryptsymmetricpasswordbasedkey;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
public final class AesCryptoPBEKey {
        // *** 1 *** Specify the encryption mode and padding explicitly.
        // *** 2 *** Use intractable cryptographic technologies that include an algorithm, a block cipher mode and padding.
        // Parameters sent to the getInstance method of the Cipher class: encryption algorithm, block cypher mode, padding
        // The following parameters in this example:encryption algorithm=AES, block cypher mode=CBC, padding=PKCS7Padding
        private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
        // The string for getting an exemplar of the class that will generate a key
        private static final String KEY_GENERATOR_MODE = "PBEWITHSHA256AND128BITAES-CBC-BC";
        // *** 3 *** Use salt when generating keys from passwords.
        // The length of the string with salt in bytes
        public static final int SALT_LENGTH_BYTES = 20;
        // *** 4 *** Use an appropriate hash iteration count when generating keys from passwords.
        // The number of mixing iterations required to generate keys using PBE
        private static final int KEY_GEN_ITERATION_COUNT = 1024;
        // *** 5 *** Use the key length that ensures strong encryption.
        // The length of the key in bits
        private static final int KEY_LENGTH_BITS = 128;
        private byte[] mIV = null;
        private byte[] mSalt = null;
        public byte[] getIV() {
                return mIV;
        }
        public byte[] getSalt() {
        return mSalt;
        }
        AesCryptoPBEKey(final byte[] iv, final byte[] salt) {
                mIV = iv;
                mSalt = salt;
        }
        AesCryptoPBEKey() {
                mIV = null;
                initSalt();
        }
        private void initSalt() {
                mSalt = new byte[SALT_LENGTH_BYTES];
                SecureRandom sr = new SecureRandom();
                sr.nextBytes(mSalt);
        }
        public final byte[] encrypt(final byte[] plain, final char[] password) {
                byte[] encrypted = null;
                try {
                        // *** 1 *** Specify the encryption mode and padding explicitly.
                        // *** 2 *** Use intractable cryptographic technologies that include an algorithm, a block cipher mode and padding.
                        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
                        // *** 3 *** Use salt when generating keys from passwords.
                        SecretKey secretKey = generateKey(password, mSalt);
                        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
                        mIV = cipher.getIV();
                        encrypted = cipher.doFinal(plain);
                } catch (NoSuchAlgorithmException e) {
                } catch (NoSuchPaddingException e) {
                } catch (InvalidKeyException e) {
                } catch (IllegalBlockSizeException e) {
                } catch (BadPaddingException e) {
                } finally {
                }
                return encrypted;
        }
        public final byte[] decrypt(final byte[] encrypted, final char[] password) {
                byte[] plain = null;
                try {
                        // *** 1 *** Specify the encryption mode and padding explicitly.
                        // *** 2 *** Use intractable cryptographic technologies that include an algorithm, a block cipher mode and padding.
                        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
                        // *** 3 *** Use salt when generating keys from passwords.
                        SecretKey secretKey = generateKey(password, mSalt);
                        IvParameterSpec ivParameterSpec = new IvParameterSpec(mIV);
                        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
                        plain = cipher.doFinal(encrypted);
                } catch (NoSuchAlgorithmException e) {
                } catch (NoSuchPaddingException e) {
                } catch (InvalidKeyException e) {
                } catch (InvalidAlgorithmParameterException e) {
                } catch (IllegalBlockSizeException e) {
                } catch (BadPaddingException e) {
                } finally {
                }
                return plain;
        }
        private static final SecretKey generateKey(final char[] password, final byte[] salt) {
                SecretKey secretKey = null;
                PBEKeySpec keySpec = null;
                try {
                        // *** 2 *** Use intractable cryptographic technologies that include an algorithm, a block cipher mode and padding.
                        // Getting an exemplar of the class that will generate a key
                        // This example includes the KeyFactory class that uses the SHA256 algorithm to generate a 128-bit AES-CBC key
                        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(KEY_GENERATOR_MODE);
                        // *** 3 *** Use salt when generating keys from passwords.
                        // *** 4 *** Use an appropriate hash iteration count when generating keys from passwords.
                        // *** 5 *** Use the key length that ensures strong encryption.
                        keySpec = new PBEKeySpec(password, salt, KEY_GEN_ITERATION_COUNT, KEY_LENGTH_BITS);
                        // Erasing password—required to make debugging more difficult and to prevent the password from appearing in the memory dump.
                        Arrays.fill(password, '?');
                        // Generating a key
                        secretKey = secretKeyFactory.generateSecret(keySpec);
                } catch (NoSuchAlgorithmException e) {
                } catch (InvalidKeySpecException e) {
                } finally {
                        keySpec.clearPassword();
                }
                return secretKey;
        }
}

Links

  1. https://mobile-security.gitbook.io/mobile-security-testing-guide/android-testing-guide/0x05d-testing-data-storage
  2. https://cwe.mitre.org/data/definitions/200.html
  3. https://cwe.mitre.org/data/definitions/311.html
  4. https://cwe.mitre.org/data/definitions/312