סודות קריפטוגרפיים בתוך הקוד

קטגוריית OWASP: MASVS-CRYPTO: קריפטוגרפיה

סקירה כללית

מפתחים משתמשים בקריפטוגרפיה כדי להגן על הסודיות והשלמות של הנתונים באמצעות אלגוריתמים חזקים. עם זאת, לעיתים קרובות לא נעשה שימוש מספיק באחסון המפתחות, ולרוב הם מוצפנים בתוך האפליקציה כמחרוזת או כמערך בייטים בקוד או בקובץ נכס כמו strings.xml. אם סודות נחשפים בקבצים כלשהם של האפליקציה, זה נוגד את העיקרון של קרקהוף, ואפשר להסיק שמודל האבטחה פגום.

השפעה

פורץ עם גישה לכלים של הנדסה הפוכה יכול לאחזר סוד שמוטמע בקוד בקלות רבה. ההשפעה עשויה להשתנות בהתאם לתנאים, אבל במקרים רבים היא מובילה לבעיות אבטחה חמורות, כמו גישה למידע אישי רגיש.

אמצעי צמצום סיכונים

כדי לפתור את הבעיה הזו, מומלץ להשתמש ב-API של KeyChain כשרוצים פרטי כניסה ברמת המערכת, או בספק 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());
}

משאבים