Перейти к содержанию

Потенциально чувствительная информация, найденная энтропийным анализом

Критичность: ИНФО

Описание

Платформа Стингрей осуществляет поиск чувствительной информации в большом количестве источников:

  • файлы приложения;
  • базы данных (частный случай файлов приложения);
  • системный журнал;
  • сетевая активность;
  • межпроцессное взаимодействие;
  • Deeplink и Applink, как частный случай межпроцессного взаимодействия;
  • код и ресурсы приложения;
  • и многие другие.

Информация в этих источниках представлена в различных форматах, каждый из которых нужно правильно анализировать. Для обнаружения чувствительной информации Стингрей использует правила — регулярные выражения, с помощью которых осуществляется поиск как в структурированных данных (JSON, XML и т. д.), так и в произвольном тексте. Однако полностью охватить все разнообразие возможных чувствительных данных не представляется возможным, именно поэтому появился поиск информации по энтропии. Поиск потенциально чувствительной информации осуществляется по строкам из данных соответствующих модулей и основан на вычислении энтропии Шеннона (также известной как информационная энтропия) для каждого фрагмента текста длиной более десяти или опционально любого другого количества символов. Другими словами, система ищет все строки и данные, которые имеют высокую энтропию и потенциально могут являться чувствительной информацией (различные токены или сессионные идентификаторы). Безусловно, многие срабатывания могут быть нерелевантны, но мы придерживаемся правила, что лучше показать информацию, чем потенциально пропустить дефект.

Рекомендации

Рекомендуется дополнительно проверить найденную информацию на предмет конфиденциальности и допустимости ее хранения на устройстве в открытом виде.

Для примера рассмотрим хранение чувствительной информации с использованием Android KeyStore, благодаря которому все рутинные задачи по хранению и применению ключей шифрования переходят под управление механизмов KeyStore.

  1. Создадим необходимые классы.

    sealed class Secret(
        private val alias: String,
        private val data: String,
        private val iv: String
    )
    
    sealed class Secret(
        private val alias: String,
        private val data: String,
        private val iv: String
    )
    
    data class DecryptionSecret(
        val alias: String,
        val data: String,
        val iv: String
    )
    
  2. Добавим «обвязку» для работы с KeyStore.

    object KeystoreHelper {
    
        private const val KEY_SIZE = 256
        private const val PROVIDER = "AndroidKeyStore"
        private const val TRANSFORMATION = "AES/CBC/NoPadding"
    
        private val NULL = null
        private const val EMPTY = ""
        private const val NEW_LINE = "\n"
    
        fun encrypt(encryptionSecret: EncryptionSecret): DecryptionSecret {
            val kg =
                KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, PROVIDER)
    
            val kps = KeyGenParameterSpec.Builder(
                encryptionSecret.alias,
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            ).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE).build()
    
            kg.init(kps)
            val sk = kg.generateKey()
    
            val c = Cipher.getInstance(TRANSFORMATION)
            c.init(Cipher.ENCRYPT_MODE, sk)
    
            val eb = c.doFinal(encryptionSecret.data.toByteArray())
            val ctB64 = Base64.encodeToString(eb, Base64.NO_PADDING)
            val ivB64 = Base64.encodeToString(c.iv, Base64.NO_PADDING)
    
            return DecryptionSecret(
                data = ctB64.replace(NEW_LINE, EMPTY),
                iv = ivB64.replace(NEW_LINE, EMPTY),
                alias = encryptionSecret.alias
            )
        }
    
        fun decrypt(decryptionSecret: DecryptionSecret): EncryptionSecret {
    
            val iv = Base64.decode(decryptionSecret.iv, Base64.NO_PADDING)
            val ct = Base64.decode(decryptionSecret.data, Base64.NO_PADDING)
    
            val ks = KeyStore.getInstance(PROVIDER)
            ks.load(NULL)
    
            val ske: KeyStore.SecretKeyEntry =
                ks.getEntry(decryptionSecret.alias, NULL) as KeyStore.SecretKeyEntry
    
            val c = Cipher.getInstance(TRANSFORMATION)
            val gps: AlgorithmParameterSpec = GCMParameterSpec(KEY_SIZE, iv)
            c.init(Cipher.DECRYPT_MODE, ske.secretKey, gps)
    
            val decryptedBytes = String(c.doFinal(ct))
    
            return EncryptionSecret(
                alias = decryptionSecret.alias,
                data = decryptedBytes
            )
        }
    
    }
    
  3. Для шифрования используем

    // prepare the data for encryption
    val encryptionSecret = EncryptionSecret(alias = "my_secret_key", data = "ABC-1920")
    // pass the data for encryption we will receive the encrypted data as output
    // you may save it somewhere like backend or shared preferences
    val encryptedData: DecryptionSecret = KeystoreHelper.encrypt(encryptionSecret)
    
    а для расшифровки

    // prepare the data for decryption
    val toDecrypt = DecryptionSecret(
        alias = "my_secret_key",
        data = "wyI5f6i5ZaJLsGQaLXeR7rpcE4Sjrj3f",
        iv = "gLjtA7en9Sm9gf1y"
    )
    // pass the above data to begin the process of decryption
    val decryptedData = KeystoreHelper.decrypt(toDecrypt)
    

Ссылки

  1. https://techenum.com/android-keystore-store-sensitive-data-in-android/
  2. https://www.cobeisfresh.com/blog/how-to-encrypt-data-on-android-with-jetpack-security
  3. https://developer.android.com/training/articles/keystore.html
К началу