Dynamiczne wczytywanie kodu

Kategoria OWASP: MASVS-CODE: Code Quality

Omówienie

Ładowanie kodu dynamicznie w aplikacji wiąże się z pewnym poziomem ryzyka, który należy ograniczyć. Osoby atakujące mogą zmodyfikować lub zastąpić kod, aby uzyskać dostęp do danych wrażliwych lub wykonać szkodliwe działania.

Wiele form ładowania kodu dynamicznego, zwłaszcza tych, które korzystają ze źródeł zewnętrznych, narusza zasady Google Play i może skutkować zawieszeniem aplikacji w Google Play.

Wpływ

Jeśli atakujący zdobędą dostęp do kodu, który zostanie wczytany do aplikacji, mogą go zmodyfikować, aby służył ich celom. Może to doprowadzić do wycieku danych i wykorzystywania kodu. Nawet jeśli atakujący nie mogą zmodyfikować kodu, aby wykonać dowolne działania, nadal mogą uszkodzić lub usunąć kod, wpływając w ten sposób na dostępność aplikacji.

Środki zaradcze

Unikaj wczytywania kodu dynamicznego

Unikaj ładowania kodu dynamicznego, chyba że jest to konieczne ze względów biznesowych. W miarę możliwości należy uwzględniać wszystkie funkcje bezpośrednio w aplikacji.

Korzystanie z zaufanych źródeł

Kod, który ma być wczytany do aplikacji, powinien być przechowywany w zaufanych lokalizacjach. Jeśli chodzi o pamięć lokalną, zalecamy używanie pamięci wewnętrznej aplikacji lub ograniczonej pamięci (w przypadku Androida 10 lub nowszego). Te lokalizacje mają środki zapobiegające bezpośredniemu dostępowi z innych aplikacji i użytkowników.

Podczas wczytywania kodu z odległych lokalizacji, takich jak adresy URL, unikaj, jeśli to możliwe, korzystania z usług zewnętrznych i przechowuj kod w ramach własnej infrastruktury zgodnie z zalecanymi najlepszymi praktykami dotyczącymi bezpieczeństwa. Jeśli musisz wczytać kod innej firmy, upewnij się, że jest to zaufany dostawca.

Wykonywanie testów integralności

Zalecamy sprawdzanie integralności, aby mieć pewność, że kod nie został zmodyfikowany. Te kontrole należy przeprowadzić przed załadowaniem kodu do aplikacji.

Podczas wczytywania zasobów zdalnych można użyć integralności zasobów podrzędnych, aby sprawdzić integralność zasobów, do których uzyskuje się dostęp.

Podczas wczytywania zasobów z zewnętrznego magazynu danych należy przeprowadzić weryfikację integralności, aby sprawdzić, czy żadne inne aplikacje nie zmodyfikowały tych danych lub kodu. Wartości skrótów plików powinny być przechowywane w bezpieczny sposób, najlepiej w zaszyfrowanej pamięci wewnętrznej.

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!");
        }
    }
}

Podpisz kod

Inną opcją na zapewnienie integralności danych jest podpisanie kodu i sprawdzenie jego podpisu przed załadowaniem. Zaletą tej metody jest to, że zapewnia integralność kodu szyfrującego, a nie tylko samego kodu, co zapewnia dodatkową ochronę przed modyfikacją.

Chociaż podpisywanie kodu zapewnia dodatkowe warstwy zabezpieczeń, należy pamiętać, że jest to bardziej złożony proces, który może wymagać dodatkowych nakładów pracy i zasobów.

Przykłady podpisywania kodu znajdziesz w sekcji Zasoby tego dokumentu.

Materiały