Динамическая загрузка кода

Категория OWASP: MASVS-CODE: качество кода

Обзор

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

Многие способы динамической загрузки кода, особенно те, которые используют удаленные источники, нарушают правила Google Play и могут привести к блокировке вашего приложения в Google Play.

Влияние

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

Предотвращение атак

Не используйте динамическую подгрузку кода

Если в этом нет необходимости, не используйте динамическую подгрузку кода. По возможности добавляйте все функции непосредственно в приложение.

Используйте надежные источники

Код, который будет загружен в приложение, должен храниться в надежных местах. Для локального хранения рекомендуется использовать внутреннее хранилище приложения или изолированное хранилище (для Android 10 и более поздних версий). Эти расположения защищены от прямого доступа из других приложений и от других пользователей.

При загрузке кода из удаленных источников, например URL, по возможности избегайте использования сторонних сервисов и храните код в собственной инфраструктуре, следуя рекомендациям по обеспечению безопасности. Если вам нужно загрузить сторонний код, убедитесь, что поставщик надежен.

Проверять целостность данных

Рекомендуется выполнять проверки целостности, чтобы убедиться, что код не был изменен. Эти проверки следует выполнять до загрузки кода в приложение.

При загрузке удаленных ресурсов можно использовать целостность подресурсов, чтобы проверить целостность полученных ресурсов.

При загрузке ресурсов из внешнего хранилища используйте проверки целостности, чтобы убедиться, что другие приложения не изменяли эти данные или код. Хеши файлов должны храниться в безопасном месте, желательно в зашифрованном виде и во внутренней памяти.

Kotlin

package com.example.myapplication

import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.IOException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException

object FileIntegrityChecker {
    @Throws(IOException::class, NoSuchAlgorithmException::class)
    fun getIntegrityHash(filePath: String?): String {
        val md = MessageDigest.getInstance("SHA-256") // You can choose other algorithms as needed
        val buffer = ByteArray(8192)
        var bytesRead: Int
        BufferedInputStream(FileInputStream(filePath)).use { fis ->
            while (fis.read(buffer).also { bytesRead = it } != -1) {
                md.update(buffer, 0, bytesRead)
            }

    }

    private fun bytesToHex(bytes: ByteArray): String {
        val sb = StringBuilder(bytes.length * 2)
        for (b in bytes) {
            sb.append(String.format("%02x", b))
        }
        return sb.toString()
    }

    @Throws(IOException::class, NoSuchAlgorithmException::class)
    fun verifyIntegrity(filePath: String?, expectedHash: String): Boolean {
        val actualHash = getIntegrityHash(filePath)
        return actualHash == expectedHash
    }

    @Throws(Exception::class)
    @JvmStatic
    fun main(args: Array<String>) {
        val filePath = "/path/to/your/file"
        val expectedHash = "your_expected_hash_value"
        if (verifyIntegrity(filePath, expectedHash)) {
            println("File integrity is valid!")
        } else {
            println("File integrity is compromised!")
        }
    }
}

Java

package com.example.myapplication;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class FileIntegrityChecker {

    public static String getIntegrityHash(String filePath) throws IOException, NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256"); // You can choose other algorithms as needed
        byte[] buffer = new byte[8192];
        int bytesRead;

        try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream(filePath))) {
            while ((bytesRead = fis.read(buffer)) != -1) {
                md.update(buffer, 0, bytesRead);
            }
        }

        byte[] digest = md.digest();
        return bytesToHex(digest);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    public static boolean verifyIntegrity(String filePath, String expectedHash) throws IOException, NoSuchAlgorithmException {
        String actualHash = getIntegrityHash(filePath);
        return actualHash.equals(expectedHash);
    }

    public static void main(String[] args) throws Exception {
        String filePath = "/path/to/your/file";
        String expectedHash = "your_expected_hash_value";

        if (verifyIntegrity(filePath, expectedHash)) {
            System.out.println("File integrity is valid!");
        } else {
            System.out.println("File integrity is compromised!");
        }
    }
}

Как подписать код

Другой способ обеспечить целостность данных – подписать код и проверить его подпись перед загрузкой. Этот метод также обеспечивает целостность хеш-кода, а не только самого кода, что обеспечивает дополнительную защиту от несанкционированного доступа.

Хотя подпись кода обеспечивает дополнительные уровни безопасности, важно учитывать, что это более сложный процесс, который может потребовать дополнительных усилий и ресурсов для успешной реализации.

Примеры подписания кода можно найти в разделе "Ресурсы" этого документа.

Ресурсы