Categoria OWASP: MASVS-STORAGE: Storage
Panoramica
Le applicazioni che hanno come target Android 10 (API 29) o versioni precedenti non applicano lo storage
isolato. Ciò significa che qualsiasi dato memorizzato nella memoria esterna può essere
accessibile da qualsiasi altra applicazione con l'autorizzazione
READ_EXTERNAL_STORAGE.
Impatto
Nelle applicazioni destinate ad Android 10 (API 29) o versioni precedenti, se i dati sensibili sono memorizzati nella memoria esterna, qualsiasi applicazione sul dispositivo con l'autorizzazione READ_EXTERNAL_STORAGE può accedervi. Ciò consente alle applicazioni dannose di accedere silenziosamente a file sensibili archiviati in modo permanente o temporaneo nello spazio di archiviazione esterno. Inoltre, poiché i contenuti dell'archivio esterno sono accessibili a qualsiasi app sul sistema, qualsiasi applicazione dannosa che dichiara anche l'autorizzazione WRITE_EXTERNAL_STORAGE può manomettere i file archiviati nell'archivio esterno, ad esempio per includere dati dannosi. Questi dati dannosi, se caricati nell'applicazione, potrebbero essere progettati per ingannare gli utenti o persino per eseguire codice.
Mitigazioni
Spazio di archiviazione isolato (Android 10 e versioni successive)
Android 10
Per le applicazioni destinate ad Android 10, gli sviluppatori possono attivare esplicitamente
lo spazio di archiviazione isolato. Puoi farlo impostando il flag
requestLegacyExternalStorage su false nel
file AndroidManifest.xml. Con lo spazio di archiviazione isolato, le applicazioni possono accedere solo
ai file che hanno creato autonomamente sull'unità di archiviazione esterna o ai tipi di file
che sono stati archiviati 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 impone l'utilizzo dell'archiviazione mirata, ovvero ignora il flag requestLegacyExternalStorage e protegge automaticamente lo spazio di archiviazione esterno delle applicazioni da accessi indesiderati.
Utilizzare l'archivio interno per i dati sensibili
Indipendentemente dalla versione di Android di destinazione, i dati sensibili di un'applicazione devono sempre essere archiviati nella memoria interna. L'accesso alla memoria interna è automaticamente limitato all'applicazione proprietaria grazie al sandboxing di Android, pertanto può essere considerato sicuro, a meno che il dispositivo non sia rooted.
Criptare i dati sensibili
Se i casi d'uso dell'applicazione richiedono la memorizzazione di dati sensibili nella memoria esterna, i dati devono essere criptati. È consigliato un algoritmo crittografico efficace, che utilizzi Android KeyStore per archiviare in modo sicuro la chiave.
In generale, la crittografia di tutti i dati sensibili è una pratica di sicurezza consigliata, indipendentemente da dove vengono archiviati.
È importante notare che la crittografia completa del disco (o la crittografia basata su file di Android 10) è una misura volta a proteggere i dati dall'accesso fisico e da altri vettori di attacco. Per questo motivo, per garantire la stessa misura di sicurezza, i dati sensibili archiviati su un dispositivo di archiviazione esterno devono essere criptati anche dall'applicazione.
Eseguire controlli di integrità
Nei casi in cui i dati o il codice devono essere caricati dalla memoria esterna nell'applicazione, sono consigliati controlli di integrità per verificare che nessun'altra applicazione abbia manomesso questi dati o questo codice. 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
- Archiviazione isolata
- READ_EXTERNAL_STORAGE
- WRITE_EXTERNAL_STORAGE
- requestLegacyExternalStorage
- Panoramica dell'archiviazione di file e dati
- Archiviazione dati (specifica per l'app)
- Crittografia
- Keystore
- Crittografia basata su file
- Crittografia completa del disco