دسته OWASP: MASVS-STORAGE: ذخیره سازی
نمای کلی
برنامههایی که Android 10 (API 29) یا پایینتر را هدف قرار میدهند ، فضای ذخیرهسازی محدوده را اعمال نمیکنند. این بدان معنی است که هر برنامه دیگری با مجوز READ_EXTERNAL_STORAGE
می تواند به هر داده ذخیره شده در حافظه خارجی دسترسی داشته باشد.
تاثیر
در برنامههایی که Android 10 (API 29) یا پایینتر را هدف قرار میدهند، اگر دادههای حساس در حافظه خارجی ذخیره شده باشد، هر برنامهای روی دستگاه با مجوز READ_EXTERNAL_STORAGE میتواند به آن دسترسی داشته باشد. این به برنامههای مخرب اجازه میدهد تا بیصدا به فایلهای حساسی که به طور دائم یا موقت در حافظه خارجی ذخیره شدهاند، دسترسی پیدا کنند. علاوه بر این، از آنجایی که هر برنامه در سیستم میتواند به محتوای موجود در حافظه خارجی دسترسی داشته باشد، هر برنامه مخربی که مجوز WRITE_EXTERNAL_STORAGE را نیز اعلام کند، میتواند فایلهای ذخیرهشده در حافظه خارجی را دستکاری کند، به عنوان مثال، دادههای مخرب را شامل شود. این داده های مخرب، اگر در برنامه بارگذاری شوند، می توانند برای فریب کاربران یا حتی اجرای کد طراحی شوند.
اقدامات کاهشی
Scoped Storage (اندروید 10 و بالاتر)
اندروید 10
برای برنامههایی که Android 10 را هدف قرار میدهند، توسعهدهندگان میتوانند صریحاً از فضای ذخیرهسازی محدوده استفاده کنند. این را می توان با تنظیم پرچم requestLegacyExternalStorage
روی false در فایل AndroidManifest.xml
به دست آورد. با فضای ذخیرهسازی دامنهدار، برنامهها فقط میتوانند به فایلهایی دسترسی داشته باشند که خودشان در حافظه خارجی ایجاد کردهاند یا انواع فایلهایی که با استفاده از MediaStore API ذخیره شدهاند، مانند صدا و تصویر. این به محافظت از حریم خصوصی و امنیت کاربر کمک می کند.
اندروید 11 به بعد
برای برنامههایی که Android 11 یا نسخههای جدیدتر را هدف قرار میدهند، سیستمعامل استفاده از فضای ذخیرهسازی محدوده را اعمال میکند ، یعنی پرچم requestLegacyExternalStorage
را نادیده میگیرد و به طور خودکار از حافظه خارجی برنامهها در برابر دسترسی ناخواسته محافظت میکند.
از حافظه داخلی برای داده های حساس استفاده کنید
صرف نظر از نسخه هدفمند اندروید، داده های حساس برنامه همیشه باید در حافظه داخلی ذخیره شوند. دسترسی به حافظه داخلی به لطف سندباکس اندروید به طور خودکار به برنامه مالک محدود می شود، بنابراین می توان آن را ایمن در نظر گرفت، مگر اینکه دستگاه روت شده باشد.
رمزگذاری داده های حساس
اگر موارد استفاده برنامه نیاز به ذخیره داده های حساس در حافظه خارجی دارد، داده ها باید رمزگذاری شوند. یک الگوریتم رمزگذاری قوی با استفاده از Android KeyStore برای ذخیره ایمن کلید توصیه می شود.
به طور کلی، رمزگذاری تمام داده های حساس یک اقدام امنیتی توصیه شده است، مهم نیست در کجا ذخیره می شود.
توجه به این نکته مهم است که رمزگذاری کامل دیسک (یا رمزگذاری مبتنی بر فایل از اندروید 10) اقدامی با هدف محافظت از داده ها در برابر دسترسی فیزیکی و سایر بردارهای حمله است. به همین دلیل، برای اعطای همان معیار امنیتی، داده های حساس ذخیره شده در حافظه خارجی باید علاوه بر این توسط برنامه رمزگذاری شوند.
انجام بررسی های یکپارچگی
در مواردی که داده یا کد باید از حافظه خارجی در برنامه بارگیری شود، بررسی یکپارچگی برای تأیید اینکه هیچ برنامه دیگری در این داده یا کد دستکاری نشده است توصیه می شود. هش های فایل ها باید به صورت ایمن، ترجیحا رمزگذاری شده و در حافظه داخلی ذخیره شوند.
کاتلین
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!")
}
}
}
جاوا
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
- مروری بر ذخیره سازی داده ها و فایل ها
- ذخیره سازی داده (ویژه برنامه)
- رمزنگاری
- فروشگاه کلید
- رمزگذاری مبتنی بر فایل
- رمزگذاری فول دیسک