Chargement de code dynamique

Catégorie OWASP : MASVS-CODE : qualité du code

Présentation

Le chargement dynamique de code dans une application introduit un niveau de risque qui doit être atténué. Les pirates peuvent potentiellement falsifier ou remplacer le code pour accéder à des données sensibles ou exécuter des actions nuisibles.

De nombreuses formes de chargement dynamique de code, en particulier celles qui utilisent des sources distantes, enfreignent les règlements de Google Play et peuvent entraîner la suspension de votre application sur Google Play.

Impact

Si des pirates parviennent à accéder au code qui sera chargé dans l'application, ils peuvent le modifier pour atteindre leurs objectifs. Cela peut entraîner l'exfiltration de données et des exploits d'exécution de code. Même si les pirates ne peuvent pas modifier le code pour effectuer des actions arbitraires de leur choix, il est toujours possible qu'ils corrompent ou suppriment le code et affectent ainsi la disponibilité de l'application.

Stratégies d'atténuation

Éviter d'utiliser le chargement dynamique de code

Sauf si cela est nécessaire pour l'entreprise, évitez le chargement dynamique de code. Dans la mesure du possible, vous devez préférer inclure toutes les fonctionnalités directement dans l'application.

Utiliser des sources fiables

Le code qui sera chargé dans l'application doit être stocké dans des emplacements fiables. En ce qui concerne le stockage local, la mémoire de stockage interne de l'application ou l'espace de stockage cloisonné (pour Android 10 et versions ultérieures) sont les emplacements recommandés. Ces emplacements disposent de mesures permettant d'éviter l'accès direct depuis d'autres applications et utilisateurs.

Lorsque vous chargez du code à partir d'emplacements distants tels que des URL, évitez d'utiliser des tiers dans la mesure du possible et stockez le code dans votre propre infrastructure, en suivant les bonnes pratiques de sécurité. Si vous devez charger du code tiers, assurez-vous que le fournisseur est fiable.

Effectuer des contrôles d'intégrité

Il est recommandé d'effectuer des contrôles d'intégrité pour s'assurer que le code n'a pas été falsifié. Ces contrôles doivent être effectués avant de charger le code dans l'application.

Lorsque vous chargez des ressources distantes, vous pouvez utiliser l'intégrité des sous-ressources pour valider l'intégrité des ressources auxquelles vous accédez.

Lorsque vous chargez des ressources à partir du stockage externe, utilisez des contrôles d'intégrité pour vérifier qu'aucune autre application n'a falsifié ces données ou ce code. Les hachages des fichiers doivent être stockés de manière sécurisée, de préférence chiffrés et dans la mémoire de stockage interne.

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

Signer le code

Une autre option pour garantir l'intégrité des données consiste à signer le code et à vérifier sa signature avant de le charger. Cette méthode présente l'avantage de garantir également l'intégrité du code de hachage, et pas seulement du code lui-même, ce qui offre une protection supplémentaire contre la falsification.

Bien que la signature de code offre des couches de sécurité supplémentaires, il est important de tenir compte du fait qu'il s'agit d'un processus plus complexe qui peut nécessiter des efforts et des ressources supplémentaires pour être mis en œuvre correctement.

Vous trouverez quelques exemples de signature de code dans la section "Ressources" de ce document.

Ressources