Dinamik Kod Yükleme

OWASP kategorisi: MASVS-CODE: Kod Kalitesi

Genel Bakış

Bir uygulamaya dinamik olarak kod yüklemek, azaltılması gereken bir risk düzeyi oluşturur. Saldırganlar, hassas verilere erişmek veya zararlı işlemler yapmak için kodu bozabilir ya da değiştirebilir.

Özellikle uzak kaynakları kullananlar olmak üzere birçok dinamik kod yükleme şekli Google Play politikalarını ihlal eder ve uygulamanızın Google Play'de askıya alınmasına neden olabilir.

Etki

Saldırganlar uygulamaya yüklenecek koda erişmeyi başarırsa bu kodu hedeflerini desteklemek için değiştirebilirler. Bu da veri sızıntılarına ve kod yürütme istismarlarına neden olabilir. Saldırganlar, istedikleri işlemleri yapmak için kodu değiştiremese bile kodu bozabilir veya kaldırabilir ve böylece uygulamanın kullanılabilirliğini etkileyebilir.

Çözümler

Dinamik kod yükleme kullanmaktan kaçının

İşle ilgili bir ihtiyaç yoksa dinamik kod yüklemekten kaçının. Mümkün olduğunda tüm işlevleri doğrudan uygulamaya dahil etmeyi tercih etmelisiniz.

Güvenilir kaynaklar kullanın

Uygulamaya yüklenecek kod, güvenilir konumlarda depolanmalıdır. Yerel depolama alanı için uygulamanın dahili depolama alanı veya kapsamlı depolama alanı (Android 10 ve sonraki sürümler için) önerilir. Bu konumlarda, diğer uygulamaların ve kullanıcıların doğrudan erişmesini engellemeye yönelik önlemler vardır.

URL gibi uzak konumlardan kod yüklerken, mümkün olduğunda üçüncü tarafları kullanmaktan kaçının ve güvenlikle ilgili en iyi uygulamaları izleyerek kodu kendi altyapınızda depolayın. Üçüncü taraf kodu yüklemeniz gerekiyorsa sağlayıcının güvenilir olduğundan emin olun.

Bütünlük kontrolleri gerçekleştirme

Kodun bozulmadığından emin olmak için bütünlük kontrolleri önerilir. Bu kontroller, uygulamaya kod yüklemeden önce yapılmalıdır.

Uzak kaynaklar yüklenirken, erişilen kaynakların bütünlüğünü doğrulamak için alt kaynak bütünlüğü kullanılabilir.

Harici depolama alanındaki kaynakları yüklerken, başka bir uygulamanın bu verilerde veya kodda değişiklik yapmadığını doğrulamak için bütünlük kontrollerini kullanın. Dosyaların karma değerleri güvenli bir şekilde, tercihen şifrelenmiş olarak ve dahili depolama alanında saklanmalıdır.

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

Kodu imzalama

Verilerin bütünlüğünü sağlamak için başka bir seçenek de kodu yüklemeden önce imzalamak ve imzasını doğrulamaktır. Bu yöntemin avantajı, yalnızca kodun değil, karma kodunun da bütünlüğünü sağlamasıdır. Bu da ek bir müdahaleye karşı koruma sağlar.

Kod imzalama ek güvenlik katmanları sağlasa da başarılı bir şekilde uygulanması için ek çaba ve kaynak gerektirebilecek daha karmaşık bir süreç olduğunu göz önünde bulundurmanız önemlidir.

Kod imzalama örneklerini bu dokümanın Kaynaklar bölümünde bulabilirsiniz.

Kaynaklar