البيانات الحسّاسة المخزَّنة في مساحة التخزين الخارجية
تنظيم صفحاتك في مجموعات
يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.
فئة OWASP: MASVS-STORAGE: مساحة التخزين
نظرة عامة
لا تفرض التطبيقات التي تستهدف Android 10 (المستوى 29 لواجهة برمجة التطبيقات) أو إصدارًا أقدم مساحة تخزين محدودة النطاق
. وهذا يعني أنّ أي بيانات مخزّنة على مساحة التخزين الخارجية يمكنلأي تطبيق آخر يملك إذن READ_EXTERNAL_STORAGE
الوصول إليها.
التأثير
في التطبيقات التي تستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 لواجهة برمجة التطبيقات) أو الإصدارات الأقدم، إذا كانت البيانات الحسّاسة
مخزّنة في مساحة التخزين الخارجية، يمكن لأي تطبيق على الجهاز لديه إذن
READ_EXTERNAL_STORAGE الوصول إليها. ويسمح ذلك للتطبيقات
الضارّة بالوصول بصمت إلى الملفات الحسّاسة التي يتم تخزينها permanently or temporarily
على وحدة التخزين الخارجية. بالإضافة إلى ذلك، بما أنّه يمكن لأي تطبيق على النظام الوصول إلى المحتوى في ملف التخزين الخارجي، يمكن لأي تطبيق ضار يعلن أيضًا عن إذن WRITE_EXTERNAL_STORAGE التلاعب بالملفات المخزّنة في ملف التخزين الخارجي، مثلاً لتضمين بيانات ضارة. إذا تم تحميل هذه البيانات الشديدة الضرر في التطبيق، يمكن أن يتم تصميمها لخداع المستخدمين أو حتى
تنفيذ الرموز البرمجية.
إجراءات التخفيف
التخزين الفرعي (نظام التشغيل Android 10 والإصدارات الأحدث)
Android 10
بالنسبة إلى التطبيقات التي تستهدف الإصدار 10 من Android، يمكن للمطوّرين تفعيل ميزة "مساحة التخزين ذات النطاق المحدّد" صراحةً. ويمكن إجراء ذلك من خلال ضبط علامة
requestLegacyExternalStorage
على false فيملف
AndroidManifest.xml
. باستخدام مساحة تخزين النطاق، يمكن للتطبيقات فقط الوصول إلى
الملفات التي أنشأها بنفسه على وحدة التخزين الخارجية أو أنواع الملفات
التي تم تخزينها باستخدام MediaStore API، مثل الصوت والفيديو. هذا النمط
تساعد في حماية خصوصية المستخدم وأمانه.
الإصدار 11 من نظام Android والإصدارات الأحدث
بالنسبة إلى التطبيقات التي تستهدف الإصدار 11 من نظام التشغيل Android أو الإصدارات الأحدث، يفرض نظام التشغيل
استخدام مساحة تخزين على نطاق واسع، أي أنّه يتجاهل
الإبلاغ عن requestLegacyExternalStorage
وتوفير الحماية تلقائيًا
التطبيقات" وحدة التخزين الخارجية من الوصول غير المرغوب فيه.
استخدام مساحة التخزين الداخلية للبيانات الحسّاسة
بغض النظر عن إصدار Android المستهدَف، يجب دائمًا تخزين بيانات التطبيق الحسّاسة
في وحدة التخزين الداخلية. الوصول إلى وحدة التخزين الداخلية
تلقائيًا على التطبيق المالك وذلك بفضل وضع الحماية في Android،
ولذلك يمكن اعتباره آمنًا، ما لم يكن الجهاز مزوّدًا بإذن الوصول إلى الجذر.
تشفير البيانات الحسّاسة
إذا كانت حالات استخدام التطبيق تتطلّب تخزين بيانات حسّاسة على مساحة التخزين الخارجية، يجب تشفير البيانات. ننصح باستخدام خوارزمية تشفير قوية، مع استخدام مخزّن مفاتيح Android لتخزين المفتاح بأمان.
بشكل عام، يُنصح بتشفير جميع البيانات الحساسة بغض النظر عن مكان تخزينها.
تجدر الإشارة إلى أنّ تشفير القرص الكامل (أو التشفير المستنِد إلى الملفات من
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!");
}
}
}
المراجع
يخضع كل من المحتوى وعيّنات التعليمات البرمجية في هذه الصفحة للتراخيص الموضحّة في ترخيص استخدام المحتوى. إنّ Java وOpenJDK هما علامتان تجاريتان مسجَّلتان لشركة Oracle و/أو الشركات التابعة لها.
تاريخ التعديل الأخير: 2025-08-04 (حسب التوقيت العالمي المتفَّق عليه)
[[["يسهُل فهم المحتوى.","easyToUnderstand","thumb-up"],["ساعَدني المحتوى في حلّ مشكلتي.","solvedMyProblem","thumb-up"],["غير ذلك","otherUp","thumb-up"]],[["لا يحتوي على المعلومات التي أحتاج إليها.","missingTheInformationINeed","thumb-down"],["الخطوات معقدة للغاية / كثيرة جدًا.","tooComplicatedTooManySteps","thumb-down"],["المحتوى قديم.","outOfDate","thumb-down"],["ثمة مشكلة في الترجمة.","translationIssue","thumb-down"],["مشكلة في العيّنات / التعليمات البرمجية","samplesCodeIssue","thumb-down"],["غير ذلك","otherDown","thumb-down"]],["تاريخ التعديل الأخير: 2025-08-04 (حسب التوقيت العالمي المتفَّق عليه)"],[],[],null,["# Sensitive Data Stored in External Storage\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-STORAGE: Storage](https://mas.owasp.org/MASVS/05-MASVS-STORAGE)\n\nOverview\n--------\n\nApplications targeting Android 10 (API 29) or lower don't enforce [scoped\nstorage](/training/data-storage#scoped-storage). This means that any data stored on the external storage can be\naccessed by any other application with the [`READ_EXTERNAL_STORAGE`](/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE)\npermission.\n\nImpact\n------\n\nIn applications targeting Android 10 (API 29) or lower, if sensitive data is\nstored on the external storage, any application on the device with the\nREAD_EXTERNAL_STORAGE permission can access it. This allows malicious\napplications to silently access sensitive files permanently or temporarily\nstored on the external storage. Additionally, since content on the external\nstorage can be accessed by any app on the system, any malicious application that\nalso declares the WRITE_EXTERNAL_STORAGE permission can tamper with files stored\non the external storage, e.g. to include malicious data. This malicious\ndata, if loaded into the application, could be designed to deceive users or even\nachieve code execution.\n\nMitigations\n-----------\n\n### Scoped Storage (Android 10 and later)\n\n##### Android 10\n\nFor applications targeting Android 10, developers can explicitly opt-in to\nscoped storage. This can be achieved by setting the\n[`requestLegacyExternalStorage`](/reference/android/R.attr#requestLegacyExternalStorage) flag to **false** in the\n`AndroidManifest.xml` file. With scoped storage, applications can only access\nfiles that they have created themselves on the external storage or files types\nthat were stored using the [MediaStore API](/reference/android/provider/MediaStore) such as Audio and Video. This\nhelps protect user privacy and security.\n\n##### Android 11 and later\n\nFor applications targeting Android 11 or later versions, the OS [enforces the\nuse of scoped storage](/about/versions/11/privacy/storage#scoped-storage), i.e. it ignores the\n[`requestLegacyExternalStorage`](/reference/android/R.attr#requestLegacyExternalStorage) flag and automatically protects\napplications' external storage from unwanted access.\n\n### Use Internal Storage for Sensitive Data\n\nRegardless of the targeted Android version, an application's sensitive data\nshould always be stored on internal storage. Access to internal storage is\nautomatically restricted to the owning application thanks to Android sandboxing,\ntherefore it can be considered secure, unless the device is rooted.\n\n### Encrypt sensitive data\n\nIf the application's use cases require storing sensitive data on the external\nstorage, the data should be encrypted. A strong encryption algorithm is\nrecommended, using the [Android KeyStore](/privacy-and-security/keystore) to safely store the key.\n\nIn general, encrypting all sensitive data is a recommended security practice, no\nmatter where it is stored.\n\nIt is important to note that full disk encryption (or file-based encryption from\nAndroid 10) is a measure aimed at protecting data from physical access and other\nattack vectors. Because of this, to grant the same security measure, sensitive\ndata held on external storage should additionally be encrypted by the\napplication.\n\n### Perform integrity checks\n\nIn cases where data or code has to be loaded from the external storage into the\napplication, integrity checks to verify that no other application has tampered\nwith this data or code are recommended. The hashes of the files should be stored\nin a secure manner, preferably encrypted and in the internal storage. \n\n### Kotlin\n\n package com.example.myapplication\n\n import java.io.BufferedInputStream\n import java.io.FileInputStream\n import java.io.IOException\n import java.security.MessageDigest\n import java.security.NoSuchAlgorithmException\n\n object FileIntegrityChecker {\n @Throws(IOException::class, NoSuchAlgorithmException::class)\n fun getIntegrityHash(filePath: String?): String {\n val md = MessageDigest.getInstance(\"SHA-256\") // You can choose other algorithms as needed\n val buffer = ByteArray(8192)\n var bytesRead: Int\n BufferedInputStream(FileInputStream(filePath)).use { fis -\u003e\n while (fis.read(buffer).also { bytesRead = it } != -1) {\n md.update(buffer, 0, bytesRead)\n }\n\n }\n\n private fun bytesToHex(bytes: ByteArray): String {\n val sb = StringBuilder()\n for (b in bytes) {\n sb.append(String.format(\"%02x\", b))\n }\n return sb.toString()\n }\n\n @Throws(IOException::class, NoSuchAlgorithmException::class)\n fun verifyIntegrity(filePath: String?, expectedHash: String): Boolean {\n val actualHash = getIntegrityHash(filePath)\n return actualHash == expectedHash\n }\n\n @Throws(Exception::class)\n @JvmStatic\n fun main(args: Array\u003cString\u003e) {\n val filePath = \"/path/to/your/file\"\n val expectedHash = \"your_expected_hash_value\"\n if (verifyIntegrity(filePath, expectedHash)) {\n println(\"File integrity is valid!\")\n } else {\n println(\"File integrity is compromised!\")\n }\n }\n }\n\n### Java\n\n package com.example.myapplication;\n\n import java.io.BufferedInputStream;\n import java.io.FileInputStream;\n import java.io.IOException;\n import java.security.MessageDigest;\n import java.security.NoSuchAlgorithmException;\n\n public class FileIntegrityChecker {\n\n public static String getIntegrityHash(String filePath) throws IOException, NoSuchAlgorithmException {\n MessageDigest md = MessageDigest.getInstance(\"SHA-256\"); // You can choose other algorithms as needed\n byte[] buffer = new byte[8192];\n int bytesRead;\n\n try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream(filePath))) {\n while ((bytesRead = fis.read(buffer)) != -1) {\n md.update(buffer, 0, bytesRead);\n }\n }\n\n byte[] digest = md.digest();\n return bytesToHex(digest);\n }\n\n private static String bytesToHex(byte[] bytes) {\n StringBuilder sb = new StringBuilder();\n for (byte b : bytes) {\n sb.append(String.format(\"%02x\", b));\n }\n return sb.toString();\n }\n\n public static boolean verifyIntegrity(String filePath, String expectedHash) throws IOException, NoSuchAlgorithmException {\n String actualHash = getIntegrityHash(filePath);\n return actualHash.equals(expectedHash);\n }\n\n public static void main(String[] args) throws Exception {\n String filePath = \"/path/to/your/file\";\n String expectedHash = \"your_expected_hash_value\";\n\n if (verifyIntegrity(filePath, expectedHash)) {\n System.out.println(\"File integrity is valid!\");\n } else {\n System.out.println(\"File integrity is compromised!\");\n }\n }\n }\n\nResources\n---------\n\n- [Scoped storage](/training/data-storage#scoped-storage)\n- [READ_EXTERNAL_STORAGE](/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE)\n- [WRITE_EXTERNAL_STORAGE](/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE)\n- [requestLegacyExternalStorage](/reference/android/R.attr#requestLegacyExternalStorage)\n- [Data and file storage overview](/training/data-storage)\n- [Data Storage (App Specific)](/training/data-storage/app-specific)\n- [Cryptography](/privacy-and-security/cryptography)\n- [Keystore](/privacy-and-security/keystore)\n- [File-Based encryption](https://source.android.com/docs/security/features/encryption/file-based)\n- [Full-Disk encryption](https://source.android.com/docs/security/features/encryption/full-disk)"]]