Dati sensibili archiviati in uno spazio di archiviazione esterno

Categoria OWASP: MASVS-STORAGE: spazio di archiviazione

Panoramica

Le app che hanno come target Android 10 (API 29) o versioni precedenti non hanno ambito applicato spazio di archiviazione. Ciò significa che qualsiasi dato archiviato nello spazio di archiviazione esterno può essere acceduto da qualsiasi altra applicazione con l'autorizzazione READ_EXTERNAL_STORAGE.

Impatto

Nelle applicazioni che hanno come target Android 10 (API 29) o versioni precedenti, se i dati sensibili sono nella memoria esterna, qualsiasi applicazione sul dispositivo con L'autorizzazione READ_EXTERNAL_STORAGE può accedervi. Ciò consente di accedere ai file sensibili in modo invisibile all'utente in modo permanente o temporaneo nella memoria esterna. Inoltre, poiché i contenuti all'esterno spazio di archiviazione accessibile da qualsiasi applicazione sul sistema, applicazione dannosa che dichiara inoltre che l'autorizzazione WRITE_EXTERNAL_STORAGE può manomettere i file archiviati sulla memoria esterna, ad esempio per includere dati dannosi. Questo malware di dati, se caricati nell'applicazione, potrebbero essere progettati per ingannare gli utenti o ottenere l'esecuzione del codice.

Mitigazioni

Archiviazione con ambito (Android 10 e versioni successive)

Android 10

Per le applicazioni destinate ad Android 10, gli sviluppatori possono attivare esplicitamente la archiviazione con ambito. A questo scopo, imposta requestLegacyExternalStorage viene impostato su false nella AndroidManifest.xml. Con lo spazio di archiviazione limitato, le applicazioni possono accedere solo ai file che hanno creato autonomamente sullo spazio di archiviazione esterno o ai tipi di file memorizzati utilizzando l'API MediaStore, ad esempio audio e video. Ciò contribuisce a proteggere la privacy e la sicurezza degli utenti.

Android 11 e versioni successive

Per le applicazioni che hanno come target Android 11 o versioni successive, il sistema operativo applica in modo forzato le l'utilizzo dell'archiviazione con ambito, ovvero ignora requestLegacyExternalStorage e protegge automaticamente delle applicazioni da accessi indesiderati.

Usa archiviazione interna per dati sensibili

Indipendentemente dalla versione di Android scelta come target, i dati sensibili di un'applicazione devono essere sempre archiviati nella memoria interna. L'accesso alla memoria interna è limitati automaticamente all'applicazione proprietaria grazie alla limitazione tramite sandbox di Android, pertanto può essere considerato sicuro, a meno che non sia rooted.

Cripta i dati sensibili

Se i casi d'uso dell'applicazione richiedono l'archiviazione di dati sensibili sul server archiviazione, i dati devono essere crittografati. Un algoritmo crittografico efficace è utilizzando il KeyStore Android per archiviare la chiave in modo sicuro.

In generale, la crittografia di tutti i dati sensibili è una pratica di sicurezza consigliata, no indipendentemente da dove sono archiviati.

È importante notare che la crittografia completa del disco (o la crittografia basata su file Android 10) è una misura volta a proteggere i dati dall'accesso fisico e da altre vettori d'attacco. Per questo motivo, per garantire la stessa misura di sicurezza, i dati sensibili memorizzati in uno spazio di archiviazione esterno devono essere inoltre criptati dall'applicazione.

Esegui controlli di integrità

Nei casi in cui i dati o il codice deve essere caricato dalla memoria esterna applicazioni, controlli di integrità per verificare che nessun'altra applicazione sia stata manomessa con questi dati o codici. Gli hash dei file devono essere archiviati in modo sicuro, preferibilmente criptati e nella memoria interna.

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

Risorse