Категория OWASP: MASVS-STORAGE: Storage.
Обзор
В приложениях, предназначенных для Android 10 (API 29) или более ранних версий, области хранения данных не используются. Это означает, что доступ к данным на внешнем накопителе может получить любое приложение с разрешением READ_EXTERNAL_STORAGE.
Влияние
Если конфиденциальные данные хранятся на внешнем накопителе, то в приложениях, предназначенных для Android 10 (API 29) или более ранних версий, доступ к ним может получить любое приложение на устройстве с разрешением READ_EXTERNAL_STORAGE. Это позволяет вредоносным приложениям незаметно получать доступ к конфиденциальным файлам, которые постоянно или временно хранятся на внешнем накопителе. Кроме того, поскольку доступ к контенту во внешнем хранилище может получить любое приложение в системе, любое вредоносное приложение, которое также запрашивает разрешение WRITE_EXTERNAL_STORAGE, может изменять файлы, хранящиеся во внешнем хранилище, например добавлять вредоносные данные. Если вредоносные данные загружаются в приложение, они могут быть предназначены для обмана пользователей или даже выполнения кода.
Предотвращение атак
Области хранения данных (Android 10 и более поздние версии)
Android 10
Разработчики приложений для Android 10 могут явно включить области хранения данных. Для этого установите флажок requestLegacyExternalStorage в значение false в файле AndroidManifest.xml. При использовании областей хранения данных приложения могут получать доступ только к файлам, которые они сами создали во внешнем хранилище, или к типам файлов, которые были сохранены с помощью MediaStore API, например к аудио и видео. Это помогает защитить конфиденциальность и безопасность пользователей.
Android 11 и более поздние версии
Для приложений, предназначенных для Android 11 или более поздних версий, ОС принудительно использует области хранения данных, то есть игнорирует флаг requestLegacyExternalStorage и автоматически защищает внешнее хранилище приложений от нежелательного доступа.
Используйте внутреннее хранилище для конфиденциальных данных
Независимо от целевой версии Android конфиденциальные данные приложения должны храниться во внутренней памяти. Доступ к внутреннему хранилищу автоматически ограничен приложением-владельцем благодаря изолированной среде Android, поэтому его можно считать безопасным, если на устройстве не получены root-права.
Шифрование конфиденциальных данных
Если в приложении необходимо хранить конфиденциальные данные на внешнем накопителе, их следует зашифровать. Рекомендуется использовать надежный алгоритм шифрования и хранить ключ в Android KeyStore.
В целом, шифрование всех конфиденциальных данных является рекомендуемой практикой безопасности, независимо от того, где они хранятся.
Важно отметить, что полное шифрование дисков (или шифрование на уровне файлов, начиная с Android 10) – это мера, направленная на защиту данных от физического доступа и других векторов атаки. Поэтому для обеспечения того же уровня безопасности конфиденциальные данные, хранящиеся на внешнем накопителе, должны быть дополнительно зашифрованы приложением.
Выполнять проверки целостности
Если данные или код необходимо загрузить из внешнего хранилища в приложение, рекомендуется проверить их целостность, чтобы убедиться, что они не были изменены другим приложением. Хеши файлов должны храниться безопасным способом, желательно в зашифрованном виде и во внутреннем хранилище.
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!");
}
}
}
Ресурсы
- Области хранения данных
- READ_EXTERNAL_STORAGE
- WRITE_EXTERNAL_STORAGE
- requestLegacyExternalStorage
- Обзор хранилища данных и файлов
- Хранилище данных (для конкретного приложения)
- Криптография
- Keystore
- Шифрование на уровне файлов
- Полное шифрование дисков