Kategoria OWASP: MASVS-STORAGE: Storage
Omówienie
Aplikacje kierowane na Androida 10 (poziom interfejsu API 29) lub niższego nie wymagają przestrzeni pamięci ograniczonej. Oznacza to, że do wszystkich danych przechowywanych na zewnętrznej pamięci masowej może uzyskać dostęp dowolna inna aplikacja z uprawnieniami READ_EXTERNAL_STORAGE
.
Wpływ
W przypadku aplikacji kierowanych na Androida 10 (interfejs API 29) lub starszego, jeśli dane wrażliwe są przechowywane w pamięci zewnętrznej, każda aplikacja na urządzeniu z uprawnieniem READ_EXTERNAL_STORAGE może uzyskać do nich dostęp. Pozwala to złośliwym aplikacjom na bezgłośny dostęp do plików poufnych przechowywanych na dysku zewnętrznym na stałe lub tymczasowo. Ponadto, ponieważ treści na zewnątrz dostęp do pamięci masowej jest możliwy dla każdej aplikacji w systemie, a także każdej szkodliwej aplikacji, deklaruje też, że uprawnienie WRITE_EXTERNAL_STORAGE może modyfikować przechowywane pliki w pamięci zewnętrznej, np. uwzględnianie złośliwych danych. Ten złośliwy kod wczytywane do aplikacji dane mogą być zaprojektowane tak, by oszukiwały użytkowników, a nawet w celu wykonania kodu.
Łagodzenie
Ilość miejsca na dane w ograniczonym zakresie (Android 10 i nowsze)
Android 10
W przypadku aplikacji kierowanych na Androida 10 deweloperzy mogą w sposób jednoznaczny wyrazić zgodę na
w zakresie miejsca na dane. Aby to zrobić, ustaw parametr
requestLegacyExternalStorage
na false w
AndroidManifest.xml
. Gdy miejsce na dane jest ograniczone, aplikacje mają dostęp tylko
utworzone przez siebie pliki w pamięci zewnętrznej lub typy plików
które były przechowywane przy użyciu interfejsu MediaStore API, takiego jak audio i wideo. Ten
pomaga chronić prywatność i bezpieczeństwo użytkowników.
Android 11 i nowsze
W przypadku aplikacji kierowanych na Androida 11 lub nowszego system operacyjny wymaga korzystania z ograniczonego miejsca na dane, czyli ignoruje flagę requestLegacyExternalStorage
i automatycznie chroni zewnętrzne miejsce na dane aplikacji przed niechcianym dostępem.
Korzystanie z pamięci wewnętrznej na potrzeby danych wrażliwych
Niezależnie od docelowej wersji Androida poufne dane aplikacji powinny być zawsze przechowywane w pamięci wewnętrznej. Dostęp do pamięci wewnętrznej jest automatycznie ograniczony do aplikacji właściciela dzięki piaskownicy Androida, dlatego można uznać, że jest bezpieczny, chyba że urządzenie ma odblokowany dostęp do roota.
Szyfrowanie danych wrażliwych
Jeśli przypadki użycia aplikacji wymagają przechowywania danych poufnych w zewnętrznej pamięci masowej, dane te powinny być zaszyfrowane. Silny algorytm szyfrowania zalecamy użycie Android KeyStore do bezpiecznego przechowywania klucza.
Zalecane jest szyfrowanie wszystkich danych wrażliwych, niezależnie od miejsca ich przechowywania.
Pamiętaj, że pełne szyfrowanie dysku (lub szyfrowanie oparte na plikach w Androida 10) to środek mający na celu ochronę danych przed fizycznym dostępem wektory ataku. Z tego powodu, aby zapewnić taki sam poziom zabezpieczeń, aplikacja powinna dodatkowo szyfrować dane poufne przechowywane na zewnętrznym urządzeniu magazynującym.
Wykonywanie testów integralności
W sytuacjach, gdy dane lub kod muszą zostać wczytane z pamięci zewnętrznej do aplikacji, testy integralności w celu potwierdzenia, że żadna inna aplikacja nie została zmodyfikowana z takimi danymi lub kodem. Hashe plików powinny być przechowywane w bezpieczny sposób, najlepiej szyfrowany w 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()
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!");
}
}
}
Materiały
- Zakres miejsca na dane
- READ_EXTERNAL_STORAGE
- WRITE_EXTERNAL_STORAGE
- requestLegacyExternalStorage
- Miejsce na dane i pliki
- Przechowywanie danych (specyficzne dla aplikacji)
- Kryptografia
- Keystore
- Szyfrowanie oparte na plikach
- Szyfrowanie całego dysku