ข้อมูลลับในการเข้ารหัสแบบฮาร์ดโค้ด

หมวดหมู่ OWASP: MASVS-CRYPTO: วิทยาการเข้ารหัสลับ

ภาพรวม

นักพัฒนาแอปใช้วิทยาการเข้ารหัสเพื่อปกป้องความลับและความสมบูรณ์ของข้อมูลโดยใช้อัลกอริทึมที่มีประสิทธิภาพ อย่างไรก็ตาม พื้นที่เก็บข้อมูลคีย์มักถูกใช้งานไม่เต็มที่ และมักพบคีย์เหล่านี้อยู่ในแอปพลิเคชันในรูปแบบสตริงหรืออาร์เรย์ไบต์ในโค้ดหรือในไฟล์ชิ้นงาน เช่น strings.xml หากมีการแสดงข้อมูลลับในไฟล์ใดๆ ของแอป การดำเนินการดังกล่าวขัดต่อหลักการของ Kerchhoff และอาจถือว่ารูปแบบการรักษาความปลอดภัยไม่สมบูรณ์

ผลกระทบ

ผู้โจมตีที่มีสิทธิ์เข้าถึงเครื่องมือวิศวกรรมย้อนกลับจะดึงข้อมูลลับที่เขียนไว้ในโค้ดได้ง่ายๆ ผลกระทบอาจแตกต่างกันไปตามเงื่อนไข แต่ในกรณีส่วนใหญ่จะทำให้เกิดปัญหาด้านความปลอดภัยที่สำคัญ เช่น การเข้าถึงข้อมูลที่ละเอียดอ่อน

การลดปัญหา

หากต้องการลดปัญหานี้ ให้พิจารณาใช้ KeyChain API เมื่อคุณต้องการข้อมูลเข้าสู่ระบบทั้งระบบ หรือผู้ให้บริการ Android Keystore เพื่ออนุญาตให้แอปแต่ละแอปจัดเก็บข้อมูลเข้าสู่ระบบของตนเองซึ่งมีเพียงแอปนั้นๆ เท่านั้นที่เข้าถึงได้

ข้อมูลโค้ดต่อไปนี้แสดงวิธีจัดเก็บและใช้คีย์แบบสมมาตรโดยใช้ KeyStore

Kotlin

private val ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"
private val ANDROID_KEY_STORE_ALIAS = "AES_KEY_DEMO"

@Throws(
    KeyStoreException::class,
    NoSuchAlgorithmException::class,
    NoSuchProviderException::class,
    InvalidAlgorithmParameterException::class
)
private fun createAndStoreSecretKey() {
    val builder: KeyGenParameterSpec.Builder = KeyGenParameterSpec.Builder(
        ANDROID_KEY_STORE_ALIAS,
        KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
    )
    val keySpec: KeyGenParameterSpec = builder
        .setKeySize(256)
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
        .setRandomizedEncryptionRequired(true)
        .build()
    val aesKeyGenerator: KeyGenerator =
        KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER)
    aesKeyGenerator.init(keySpec)
    val key: SecretKey = aesKeyGenerator.generateKey()
}

@Throws(
    KeyStoreException::class,
    UnrecoverableEntryException::class,
    NoSuchAlgorithmException::class,
    CertificateException::class,
    IOException::class,
    NoSuchPaddingException::class,
    InvalidKeyException::class,
    IllegalBlockSizeException::class,
    BadPaddingException::class
)
private fun encryptWithKeyStore(plainText: String): ByteArray? {
    // Initialize KeyStore
    val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER)
    keyStore.load(null)
    // Retrieve the key with alias androidKeyStoreAlias created before
    val keyEntry: KeyStore.SecretKeyEntry =
        keyStore.getEntry(ANDROID_KEY_STORE_ALIAS, null) as KeyStore.SecretKeyEntry
    val key: SecretKey = keyEntry.secretKey
    // Use the secret key at your convenience
    val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.ENCRYPT_MODE, key)
    return cipher.doFinal(plainText.toByteArray())
}

Java

static private final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
static private final String ANDROID_KEY_STORE_ALIAS = "AES_KEY_DEMO";

private void createAndStoreSecretKey() throws KeyStoreException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
    KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
        ANDROID_KEY_STORE_ALIAS,
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
    KeyGenParameterSpec keySpec = builder
        .setKeySize(256)
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
        .setRandomizedEncryptionRequired(true)
        .build();
    KeyGenerator aesKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER);
    aesKeyGenerator.init(keySpec);
    SecretKey key = aesKeyGenerator.generateKey();
}

private byte[] encryptWithKeyStore(final String plainText) throws KeyStoreException, UnrecoverableEntryException, NoSuchAlgorithmException, CertificateException, IOException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    // Initialize KeyStore
    KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
    keyStore.load(null);
    // Retrieve the key with alias ANDROID_KEY_STORE_ALIAS created before
    KeyStore.SecretKeyEntry keyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ANDROID_KEY_STORE_ALIAS, null);
    SecretKey key = keyEntry.getSecretKey();
    // Use the secret key at your convenience
    final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return cipher.doFinal(plainText.getBytes());
}

แหล่งข้อมูล