Download Manager ที่ไม่ปลอดภัย
จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน
บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ
หมวดหมู่ OWASP: MASVS-NETWORK: การสื่อสารผ่านเครือข่าย
ภาพรวม
DownloadManager เป็นบริการของระบบที่เปิดตัวใน API ระดับ 9 ซึ่งจัดการการดาวน์โหลด HTTP ที่ใช้เวลานาน และอนุญาตให้แอปพลิเคชันดาวน์โหลดไฟล์เป็นงานในเบื้องหลังได้ API ของบริการนี้จะจัดการการโต้ตอบ HTTP และพยายามดาวน์โหลดอีกครั้งหลังจากดำเนินการไม่สำเร็จ หรือเมื่อการเชื่อมต่อมีการเปลี่ยนแปลงและการรีบูตระบบ
DownloadManager มีจุดอ่อนด้านความปลอดภัยที่ทำให้เป็นตัวเลือกที่ไม่ปลอดภัยสำหรับการจัดการการดาวน์โหลดในแอปพลิเคชัน Android
(1) CVE ในผู้ให้บริการดาวน์โหลด
ในปี 2018 พบ CVE 3 รายการและทำการแก้ไขใน DownloadProvider สรุปของแต่ละวิธีมีดังนี้ (ดูรายละเอียดทางเทคนิค)
- การข้ามสิทธิ์ผู้ให้บริการดาวน์โหลด – เมื่อไม่ได้รับอนุญาต แอปที่เป็นอันตรายจะดึงรายการทั้งหมดจากผู้ให้บริการดาวน์โหลดได้ ซึ่งอาจรวมถึงข้อมูลที่อาจละเอียดอ่อน เช่น ชื่อไฟล์, คำอธิบาย, ชื่อ, เส้นทาง, URL รวมถึงสิทธิ์ในการอ่าน/เขียนแบบเต็มสำหรับไฟล์ที่ดาวน์โหลดทั้งหมด แอปที่เป็นอันตรายอาจทำงานในเบื้องหลัง ตรวจสอบการดาวน์โหลดทั้งหมดและรั่วไหลเนื้อหาจากระยะไกล หรือแก้ไขไฟล์ขณะดำเนินการก่อนที่จะมีผู้ขอเข้าถึงที่ถูกต้อง ซึ่งอาจทำให้ผู้ใช้ปฏิเสธการให้บริการสำหรับแอปพลิเคชันหลัก รวมถึงดาวน์โหลดอัปเดตไม่ได้
- ดาวน์โหลดการแทรก SQL ของผู้ให้บริการ – แอปพลิเคชันที่เป็นอันตรายซึ่งไม่มีสิทธิ์เรียกข้อมูลรายการทั้งหมดจากผู้ให้บริการดาวน์โหลดผ่านช่องโหว่ในการแทรก SQL นอกจากนี้ แอปพลิเคชันที่มีสิทธิ์จำกัด เช่น
android.permission.INTERNET
ยังเข้าถึงเนื้อหาทั้งหมดของฐานข้อมูลจาก URI ที่ต่างกันได้ด้วย ระบบอาจดึงข้อมูลที่มีความละเอียดอ่อน เช่น ชื่อไฟล์ คำอธิบาย ชื่อ เส้นทาง URL และอาจเข้าถึงเนื้อหาที่ดาวน์โหลดได้ด้วย ทั้งนี้ขึ้นอยู่กับสิทธิ์
- การเปิดเผยข้อมูลส่วนหัวของคำขอของผู้ให้บริการดาวน์โหลด – แอปพลิเคชันที่เป็นอันตรายซึ่งได้รับสิทธิ์
android.permission.INTERNET
จะสามารถดึงข้อมูลทั้งหมดออกจากตารางส่วนหัวของคำขอของผู้ให้บริการดาวน์โหลด ส่วนหัวเหล่านี้อาจมีข้อมูลที่ละเอียดอ่อน เช่น คุกกี้เซสชันหรือส่วนหัวการตรวจสอบสิทธิ์สําหรับการดาวน์โหลดที่เริ่มต้นจากเบราว์เซอร์ Android หรือ Google Chrome รวมถึงแอปพลิเคชันอื่นๆ ซึ่งอาจทำให้ผู้โจมตีแอบอ้างเป็นผู้ใช้บนแพลตฟอร์มใดก็ได้ที่ดึงข้อมูลที่มีความละเอียดอ่อนของผู้ใช้
(2) สิทธิ์ที่เป็นอันตราย
DownloadManager ใน API ระดับต่ำกว่า 29 ต้องใช้สิทธิ์ที่เป็นอันตราย android.permission.WRITE_EXTERNAL_STORAGE
สำหรับ API ระดับ 29 ขึ้นไป คุณไม่จำเป็นต้องมีสิทธิ์ android.permission.WRITE_EXTERNAL_STORAGE
แต่ URI ต้องอ้างอิงถึงเส้นทางภายในไดเรกทอรีที่แอปพลิเคชันเป็นเจ้าของหรือเส้นทางภายในไดเรกทอรี "ดาวน์โหลด" ระดับบนสุด
(3) พึ่งพา Uri.parse()
DownloadManager จะใช้เมธอด Uri.parse()
เพื่อแยกวิเคราะห์ตำแหน่งของการดาวน์โหลดที่ขอ คลาส Uri
มีประโยชน์น้อยต่อการตรวจสอบอินพุตที่ไม่น่าเชื่อถือ
ผลกระทบ
การใช้ DownloadManager อาจทำให้เกิดช่องโหว่ด้วยการใช้ประโยชน์จากสิทธิ์ WRITE ในที่จัดเก็บข้อมูลภายนอก เนื่องจากสิทธิ์ android.permission.WRITE_EXTERNAL_STORAGE อนุญาตให้เข้าถึงที่เก็บข้อมูลภายนอกได้แบบกว้าง ผู้โจมตีจึงอาจแก้ไขไฟล์และการดาวน์โหลดโดยที่ผู้ใช้ไม่รู้ตัว ติดตั้งแอปที่อาจเป็นอันตราย ปฏิเสธบริการแก่แอปหลัก หรือทำให้แอปขัดข้อง ผู้ไม่ประสงค์ดียังอาจดัดแปลงสิ่งที่ส่งไปยัง Uri.parse() เพื่อทําให้ผู้ใช้ดาวน์โหลดไฟล์ที่เป็นอันตรายได้
การลดปัญหา
แทนที่จะใช้ DownloadManager ให้ตั้งค่าการดาวน์โหลดในแอปโดยตรงโดยใช้ไคลเอ็นต์ HTTP (เช่น Cronet) ตัวจัดตารางเวลา/ตัวจัดการกระบวนการ และวิธีตรวจสอบการลองอีกครั้งหากเครือข่ายขาดการเชื่อมต่อ เอกสารประกอบของไลบรารีจะมีลิงก์ไปยังแอปตัวอย่าง รวมถึงวิธีการในการใช้งาน
หากแอปพลิเคชันจำเป็นต้องจัดการการกำหนดเวลากระบวนการ เรียกใช้การดาวน์โหลดในเบื้องหลัง หรือลองสร้างการดาวน์โหลดอีกครั้งหลังจากเครือข่ายขาดการเชื่อมต่อ ให้พิจารณารวม WorkManager
และ ForegroundServices
โค้ดตัวอย่างสำหรับการตั้งค่าการดาวน์โหลดโดยใช้ Cronet มีดังนี้ ซึ่งนำมาจาก codelab ของ 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 UTC
[[["เข้าใจง่าย","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 UTC"],[],[],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)"]]