تطبيق إدارة التنزيل غير آمن
تنظيم صفحاتك في مجموعات
يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.
فئة OWASP: MASVS-NETWORK: Network Communication
نظرة عامة
DownloadManager هي خدمة نظام تم تقديمها في المستوى 9 لواجهة برمجة التطبيقات. ويعالج
عمليات تنزيل HTTP التي تستغرق وقتًا طويلاً ويسمح للتطبيقات بتنزيل الملفات كأحد
المهام التي تعمل في الخلفية. تتعامل واجهة برمجة التطبيقات مع تفاعلات HTTP وتعيد تنزيل الملفات بعد
الأخطاء أو عند تغيير الاتصال وإعادة تشغيل النظام.
يتضمّن DownloadManager نقاط ضعف ذات صلة بالأمان تجعله خيارًا غير آمن
لإدارة عمليات التنزيل في تطبيقات Android.
(1) نقاط ضعف CVE في مقدّم خدمة التنزيل
في عام 2018، تم العثور على ثلاثة CVE وإصلاحها في Download
Provider. في ما يلي ملخّص لكلّ منها (اطّلِع على التفاصيل الفنية).
- تجاوز إذن مقدّم خدمة التنزيل: بدون الأذونات الممنوحة، يمكن لتطبيق ضارٍ استرجاع جميع الإدخالات من مقدّم خدمة التنزيل، والتي يمكن أن تشمل معلومات حسّاسة محتملة، مثل أسماء الملفات والأوصاف والعناوين والمسارات وعناوين URL، بالإضافة إلى أذونات القراءة/الكتابة الكاملة لجميع الملفات التي تم تنزيلها. يمكن أن يعمل تطبيق ضار في الخلفية، ويتتبّع جميع عمليات التنزيل ويسرب محتوياتها عن بُعد، أو يعدّل الملفات أثناء التشغيل قبل أن يتم الوصول إليها من قِبل المُقدّم الشرعي للطلب. وقد يؤدي ذلك إلى حجب الخدمة
للمستخدم في التطبيقات الأساسية، بما في ذلك عدم التمكّن من تنزيل التحديثات.
- Download Provider SQL Injection – يمكن لتطبيق ضار ليس لديه أذونات استرداد جميع الإدخالات من موفر التنزيل، بسبب ثغرة إدخال SQL. بالإضافة إلى ذلك، يمكن للتطبيقات التي تمتلك أذونات محدودة، مثل
android.permission.INTERNET
، الوصول أيضًا إلى كل محتويات
قاعدة البيانات من عنوان URI مختلف. يمكن استرداد معلومات يُحتمل أن تكون حسّاسة، مثل أسماء
الملفات وأوصافها وعناوينها ومسارات الوصول إليها وعناوين URL، وقد يكون من الممكن أيضًا الوصول إلى المحتوى الذي تم تنزيله استنادًا إلى
الأذونات.
- تنزيل الإفصاح عن عناوين طلبات مقدِّم الخدمة: يمكن للتطبيق الضار الذي تم منحه إذن
android.permission.INTERNET
استرداد جميع الإدخالات من جدول عناوين طلبات موفّر التنزيل. وقد تتضمّن هذه الرؤوس معلومات حسّاسة، مثل ملفات تعريف الارتباط الخاصة بالجلسة
أو رؤوس المصادقة لأي عملية تنزيل يتم بدءها من متصفّح Android أو
Google Chrome، من بين تطبيقات أخرى. وقد يسمح ذلك للمهاجمين بدسِّ السم في طعام المستخدمين، أي
انتحال هويتهم على أي منصة تم فيها
الحصول على بياناتهم الحسّاسة.
(2) الأذونات الخطيرة
يتطلب DownloadManager في مستويات واجهة برمجة التطبيقات الأقل من 29 أذونات خطيرة –
android.permission.WRITE_EXTERNAL_STORAGE
. بالنسبة إلى المستوى 29
أو المستويات الأعلى لواجهة برمجة التطبيقات، android.permission.WRITE_EXTERNAL_STORAGE
لا تكون الأذونات مطلوبة، ولكن يجب أن يشير عنوان URL إلى مسار ضمن الкатаلوگات التي يملكها التطبيق أو مسار ضمن الدليل "عمليات التنزيل"
على مستوى أعلى.
(3) الاعتماد على Uri.parse()
تعتمد أداة DownloadManager على الطريقة Uri.parse()
لتحليل موقع التنزيل المطلوب. من أجل تحسين الأداء، لا تطبّق فئة Uri
سوى فحص بسيط أو لا تفحص على الإطلاق الإدخال غير الموثوق به.
التأثير
قد يؤدي استخدام DownloadManager إلى ثغرات أمنية من خلال استغلال أذونات
الكتابة في وحدة التخزين الخارجية. بما أنّ أذونات android.permission.WRITE_EXTERNAL_STORAGE تسمح بالوصول الواسع النطاق إلى
مساحة التخزين الخارجية، من الممكن للمهاجم أن يعدّل الملفات
وعمليات التنزيل بدون تنبيه أو أن يثبِّت تطبيقات قد تكون ضارة أو يمنع الوصول إلى التطبيقات الأساسية
أو يتسبّب في تعطُّل التطبيقات. يمكن للجهات الفاعلة الضارّة أيضًا التلاعب بما يتم إرساله إلى دالة
Uri.parse() لحثّ المستخدم على تنزيل ملف ضار.
إجراءات التخفيف
بدلاً من استخدام DownloadManager، يمكنك إعداد عمليات التنزيل مباشرةً في تطبيقك باستخدام
عميل HTTP (مثل Cronet) وأداة جدولة/مدير العمليات وطريقة لضمان
إعادة المحاولة في حال فقدان الشبكة. تتضمّن مستندات المكتبة
رابطًا يؤدي إلى نموذج تطبيق بالإضافة إلى تعليمات حول كيفية
تنفيذه.
إذا كان تطبيقك يتطلّب إمكانية إدارة جدولة العمليات أو تنفيذ عمليات تنزيل في الخلفية أو إعادة محاولة إجراء عملية التنزيل بعد فقدان شبكة الاتصال، ننصحك بتضمين WorkManager
وForegroundServices
.
في ما يلي مثال على الرمز البرمجي لإعداد عملية تنزيل باستخدام Cronet، وهو مأخوذ من الدرس التطبيقي حول Cronet.
Kotlin
override suspend fun downloadImage(url: String): ImageDownloaderResult {
val startNanoTime = System.nanoTime()
return suspendCoroutine {
cont ->
val request = engine.newUrlRequestBuilder(url, object: ReadToMemoryCronetCallback() {
override fun onSucceeded(
request: UrlRequest,
info: UrlResponseInfo,
bodyBytes: ByteArray) {
cont.resume(ImageDownloaderResult(
successful = true,
blob = bodyBytes,
latency = Duration.ofNanos(System.nanoTime() - startNanoTime),
wasCached = info.wasCached(),
downloaderRef = this@CronetImageDownloader))
}
override fun onFailed(
request: UrlRequest,
info: UrlResponseInfo,
error: CronetException
) {
Log.w(LOGGER_TAG, "Cronet download failed!", error)
cont.resume(ImageDownloaderResult(
successful = false,
blob = ByteArray(0),
latency = Duration.ZERO,
wasCached = info.wasCached(),
downloaderRef = this@CronetImageDownloader))
}
}, executor)
request.build().start()
}
}
Java
@Override
public CompletableFuture<ImageDownloaderResult> downloadImage(String url) {
long startNanoTime = System.nanoTime();
return CompletableFuture.supplyAsync(() -> {
UrlRequest.Builder requestBuilder = engine.newUrlRequestBuilder(url, new ReadToMemoryCronetCallback() {
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info, byte[] bodyBytes) {
return ImageDownloaderResult.builder()
.successful(true)
.blob(bodyBytes)
.latency(Duration.ofNanos(System.nanoTime() - startNanoTime))
.wasCached(info.wasCached())
.downloaderRef(CronetImageDownloader.this)
.build();
}
@Override
public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
Log.w(LOGGER_TAG, "Cronet download failed!", error);
return ImageDownloaderResult.builder()
.successful(false)
.blob(new byte[0])
.latency(Duration.ZERO)
.wasCached(info.wasCached())
.downloaderRef(CronetImageDownloader.this)
.build();
}
}, executor);
UrlRequest urlRequest = requestBuilder.build();
urlRequest.start();
return urlRequest.getResult();
});
}
المراجع
يخضع كل من المحتوى وعيّنات التعليمات البرمجية في هذه الصفحة للتراخيص الموضحّة في ترخيص استخدام المحتوى. إنّ Java وOpenJDK هما علامتان تجاريتان مسجَّلتان لشركة Oracle و/أو الشركات التابعة لها.
تاريخ التعديل الأخير: 2025-07-26 (حسب التوقيت العالمي المتفَّق عليه)
[[["يسهُل فهم المحتوى.","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-07-26 (حسب التوقيت العالمي المتفَّق عليه)"],[],[],null,["# Unsafe Download Manager\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-NETWORK: Network Communication](https://mas.owasp.org/MASVS/08-MASVS-NETWORK)\n\nOverview\n--------\n\nDownloadManager is a system service introduced in API level 9. It handles\nlong-running HTTP downloads and allows applications to download files as a\nbackground task. Its API handles HTTP interactions and retries downloads after\nfailures or across connectivity changes and system reboots.\n\nDownloadManager has security relevant weaknesses that make it an insecure choice\nfor managing downloads in Android applications.\n\n**(1) CVEs in Download Provider**\n\nIn 2018, three [CVEs](https://ioactive.com/multiple-vulnerabilities-in-androids-download-provider-cve-2018-9468-cve-2018-9493-cve-2018-9546/) were found and patched in Download\nProvider. A summary of each follows (see [technical details](https://ioactive.com/multiple-vulnerabilities-in-androids-download-provider-cve-2018-9468-cve-2018-9493-cve-2018-9546/)).\n\n- **Download Provider Permission Bypass** -- With no granted permissions, a malicious app could retrieve all entries from the Download Provider, which could include potentially sensitive information such as file names, descriptions, titles, paths, URLs, as well as full READ/WRITE permissions to all downloaded files. A malicious app could run in the background, monitoring all downloads and leaking their contents remotely, or modifying the files on-the-fly before they are accessed by the legitimate requester. This could cause a denial-of-service for the user for core applications, including the inability to download updates.\n- **Download Provider SQL Injection** -- Through a SQL injection vulnerability, a malicious application with no permissions could retrieve all entries from the Download Provider. Also, applications with limited permissions, such as [`android.permission.INTERNET`](http://go/android-dev/reference/android/Manifest.permission#INTERNET), could also access all database contents from a different URI. Potentially sensitive information such as file names, descriptions, titles, paths, URLs could be retrieved, and, depending on permissions, access to downloaded contents may be possible as well.\n- **Download Provider Request Headers Information Disclosure** -- A malicious application with the [`android.permission.INTERNET`](http://go/android-dev/reference/android/Manifest.permission#INTERNET) permission granted could retrieve all entries from the Download Provider request headers table. These headers may include sensitive information, such as session cookies or authentication headers, for any download started from the Android Browser or Google Chrome, among other applications. This could allow an attacker to impersonate the user on any platform from which sensitive user data was obtained.\n\n**(2) Dangerous Permissions**\n\nDownloadManager in API levels lower than 29 requires dangerous permissions --\n[`android.permission.WRITE_EXTERNAL_STORAGE`](http://go/android-dev/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE). For API level 29\nand higher, [`android.permission.WRITE_EXTERNAL_STORAGE`](http://go/android-dev/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE)\npermissions are not required, but the URI must refer to a path within the\ndirectories owned by the application or a path within the top-level \"Downloads\"\ndirectory.\n\n**(3) Reliance on** `Uri.parse()`\n\nDownloadManager relies on the `Uri.parse()` method to parse the location of the\nrequested download. In the interest of performance, the `Uri` class applies\nlittle to no validation on untrusted input.\n\nImpact\n------\n\nUsing DownloadManager may lead to vulnerabilities through the exploitation of\nWRITE permissions to external storage. Since\nandroid.permission.WRITE_EXTERNAL_STORAGE permissions allow broad access to\nexternal storage, it is possible for an attacker to silently modify files and\ndownloads, install potentially malicious apps, deny service to core apps, or\ncause apps to crash. Malicious actors could also manipulate what is sent to\nUri.parse() to cause the user to download a harmful file.\n\nMitigations\n-----------\n\nInstead of using DownloadManager, set up downloads directly in your app using an\nHTTP client (such as Cronet), a process scheduler/manager, and a way to ensure\nretries if there is network loss. The [documentation of the library](/develop/connectivity/cronet) includes\na link to a [sample](https://github.com/GoogleChromeLabs/cronet-sample) app as well as [instructions](/develop/connectivity/cronet/start) on how\nto implement it.\n\nIf your application requires the ability to manage process scheduling, run\ndownloads in the background, or retry establishing the download after network\nloss, then consider including [`WorkManager`](/reference/androidx/work/WorkManager) and\n[`ForegroundServices`](/develop/background-work/services/foreground-services).\n\nExample code for setting up a download using Cronet is as follows, taken from\nthe Cronet [codelab](/codelabs/cronet#8). \n\n### Kotlin\n\n override suspend fun downloadImage(url: String): ImageDownloaderResult {\n val startNanoTime = System.nanoTime()\n return suspendCoroutine {\n cont -\u003e\n val request = engine.newUrlRequestBuilder(url, object: ReadToMemoryCronetCallback() {\n override fun onSucceeded(\n request: UrlRequest,\n info: UrlResponseInfo,\n bodyBytes: ByteArray) {\n cont.resume(ImageDownloaderResult(\n successful = true,\n blob = bodyBytes,\n latency = Duration.ofNanos(System.nanoTime() - startNanoTime),\n wasCached = info.wasCached(),\n downloaderRef = this@CronetImageDownloader))\n }\n override fun onFailed(\n request: UrlRequest,\n info: UrlResponseInfo,\n error: CronetException\n ) {\n Log.w(LOGGER_TAG, \"Cronet download failed!\", error)\n cont.resume(ImageDownloaderResult(\n successful = false,\n blob = ByteArray(0),\n latency = Duration.ZERO,\n wasCached = info.wasCached(),\n downloaderRef = this@CronetImageDownloader))\n }\n }, executor)\n request.build().start()\n }\n }\n\n### Java\n\n @Override\n public CompletableFuture\u003cImageDownloaderResult\u003e downloadImage(String url) {\n long startNanoTime = System.nanoTime();\n return CompletableFuture.supplyAsync(() -\u003e {\n UrlRequest.Builder requestBuilder = engine.newUrlRequestBuilder(url, new ReadToMemoryCronetCallback() {\n @Override\n public void onSucceeded(UrlRequest request, UrlResponseInfo info, byte[] bodyBytes) {\n return ImageDownloaderResult.builder()\n .successful(true)\n .blob(bodyBytes)\n .latency(Duration.ofNanos(System.nanoTime() - startNanoTime))\n .wasCached(info.wasCached())\n .downloaderRef(CronetImageDownloader.this)\n .build();\n }\n @Override\n public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {\n Log.w(LOGGER_TAG, \"Cronet download failed!\", error);\n return ImageDownloaderResult.builder()\n .successful(false)\n .blob(new byte[0])\n .latency(Duration.ZERO)\n .wasCached(info.wasCached())\n .downloaderRef(CronetImageDownloader.this)\n .build();\n }\n }, executor);\n UrlRequest urlRequest = requestBuilder.build();\n urlRequest.start();\n return urlRequest.getResult();\n });\n }\n\nResources\n---------\n\n- [Main documentation page for DownloadManager](/reference/android/app/DownloadManager)\n- [Report for DownloadManager CVEs](https://ioactive.com/multiple-vulnerabilities-in-androids-download-provider-cve-2018-9468-cve-2018-9493-cve-2018-9546/)\n- [Android Permission Bypass CVE 2018-9468](https://ioactive.com/wp-content/uploads/2019/04/IOActive-Security-Advisory-Androids-Download-Provider-Permission-Bypass-CVE-2018-9468.pdf)\n- [Android Download Provider SQL Injection CVE-2018- 9493](https://act-on.ioactive.com/acton/attachment/34793/f-722b41b4-7aff-4b35-9925-c221a217744d/1/-/-/-/-/cve-2018-9493.pdf)\n- [Android Download Provider Permission Bypass CVE2018-9468](https://act-on.ioactive.com/acton/attachment/34793/f-3b8bb46b-d105-4efd-97a1-9970bfa6928b/1/-/-/-/-/cve-2018-9546.pdf)\n- [Main documentation page for Cronet](/develop/connectivity/cronet)\n- [Instructions for using Cronet in an application](/develop/connectivity/cronet/start#java)\n- [Sample Cronet implementation](https://github.com/GoogleChromeLabs/cronet-sample)\n- [Documentation for Uri](/reference/android/net/Uri)\n- [Documentation for ForegroundService](/develop/background-work/services/foreground-services)\n- [Documentation for WorkManager](/reference/androidx/work/WorkManager)"]]