Trình quản lý tải xuống không an toàn
Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Danh mục OWASP: MASVS-NETWORK: Giao tiếp qua mạng
Tổng quan
DownloadManager là một dịch vụ hệ thống được giới thiệu trong API cấp 9. Thư viện này xử lý các lượt tải xuống HTTP diễn ra trong thời gian dài và cho phép các ứng dụng tải tệp xuống dưới dạng tác vụ trong nền. API của ứng dụng này xử lý các lượt tương tác HTTP và thử tải xuống lại sau khi xảy ra lỗi hoặc khi có thay đổi về kết nối và khởi động lại hệ thống.
DownloadManager có các điểm yếu liên quan đến bảo mật khiến ứng dụng này trở thành một lựa chọn không an toàn để quản lý nội dung tải xuống trong các ứng dụng Android.
(1) CVE trong Trình cung cấp nội dung tải xuống
Năm 2018, chúng tôi đã phát hiện và vá 3 CVE trong Trình cung cấp tải xuống. Dưới đây là nội dung tóm tắt từng nội dung (xem thông tin kỹ thuật chi tiết).
- Bỏ qua quyền của trình cung cấp tải xuống – Khi không được cấp các quyền nào, ứng dụng độc hại có thể truy xuất mọi mục nhập từ Trình cung cấp tải xuống. Những ứng dụng này có thể bao gồm thông tin nhạy cảm có thể chứa thông tin nhạy cảm như tên tệp, nội dung mô tả, tiêu đề, đường dẫn, URL cũng như quyền ĐỌC/GHI đầy đủ đối với mọi tệp đã tải xuống. Ứng dụng độc hại có thể chạy trong nền, theo dõi tất cả nội dung tải xuống và làm rò rỉ nội dung từ xa, hoặc sửa đổi các tệp một cách nhanh chóng trước khi người yêu cầu hợp pháp truy cập vào các tệp đó. Điều này có thể khiến người dùng gặp phải tình trạng từ chối dịch vụ đối với các ứng dụng chính, bao gồm cả việc không thể tải bản cập nhật xuống.
- Chèn SQL vào Trình cung cấp nội dung tải xuống – Thông qua lỗ hổng chèn SQL, một ứng dụng độc hại không có quyền có thể truy xuất tất cả mục nhập từ Trình cung cấp nội dung tải xuống. Ngoài ra, các ứng dụng có quyền hạn chế, chẳng hạn như
android.permission.INTERNET
, cũng có thể truy cập vào tất cả nội dung cơ sở dữ liệu từ một URI khác. Có thể truy xuất thông tin có thể nhạy cảm như tên tệp, nội dung mô tả, tiêu đề, đường dẫn, URL và tuỳ thuộc vào quyền, có thể truy cập vào nội dung đã tải xuống.
- Tiết lộ thông tin về tiêu đề yêu cầu của Nhà cung cấp nội dung tải xuống – Một ứng dụng độc hại được cấp quyền
android.permission.INTERNET
có thể truy xuất tất cả các mục từ bảng tiêu đề yêu cầu của Nhà cung cấp nội dung tải xuống. Các tiêu đề này có thể bao gồm thông tin nhạy cảm, chẳng hạn như cookie phiên hoặc tiêu đề xác thực, đối với mọi nội dung tải xuống bắt đầu từ Trình duyệt Android hoặc Google Chrome, cùng với các ứng dụng khác. Điều này có thể cho phép kẻ tấn công mạo danh người dùng trên bất kỳ nền tảng nào mà dữ liệu nhạy cảm của người dùng được lấy từ đó.
(2) Các quyền nguy hiểm
DownloadManager ở các cấp độ API thấp hơn 29 yêu cầu các quyền nguy hiểm – android.permission.WRITE_EXTERNAL_STORAGE
. Đối với API cấp 29 trở lên, bạn không bắt buộc phải có quyền android.permission.WRITE_EXTERNAL_STORAGE
, nhưng URI phải tham chiếu đến một đường dẫn trong các thư mục do ứng dụng sở hữu hoặc một đường dẫn trong thư mục "Tải xuống" cấp cao nhất.
(3) Sự phụ thuộc vào Uri.parse()
DownloadManager dựa vào phương thức Uri.parse()
để phân tích cú pháp vị trí của nội dung tải xuống được yêu cầu. Để đảm bảo hiệu suất, lớp Uri
áp dụng ít hoặc không xác thực dữ liệu đầu vào không đáng tin cậy.
Tác động
Việc sử dụng DownloadManager có thể dẫn đến các lỗ hổng bảo mật thông qua việc lợi dụng quyền GHI vào bộ nhớ ngoài. Vì quyền android.permission.WRITE_EXTERNAL_STORAGE cho phép quyền truy cập rộng rãi vào bộ nhớ ngoài, nên kẻ tấn công có thể âm thầm sửa đổi các tệp và nội dung tải xuống, cài đặt các ứng dụng có khả năng độc hại, từ chối dịch vụ cho các ứng dụng cốt lõi hoặc khiến ứng dụng gặp sự cố. Các tác nhân độc hại cũng có thể thao túng nội dung được gửi đến Uri.parse() để khiến người dùng tải một tệp gây hại xuống.
Giải pháp giảm thiểu
Thay vì sử dụng DownloadManager, hãy thiết lập tính năng tải xuống trực tiếp trong ứng dụng bằng cách sử dụng ứng dụng HTTP (chẳng hạn như Cronet), trình lập lịch biểu/trình quản lý quy trình và một cách để đảm bảo thử lại nếu bị mất mạng. Tài liệu về thư viện bao gồm một đường liên kết đến ứng dụng mẫu cũng như hướng dẫn về cách triển khai ứng dụng đó.
Nếu ứng dụng của bạn yêu cầu khả năng quản lý việc lên lịch quy trình, chạy tải xuống ở chế độ nền hoặc thử lại việc thiết lập tải xuống sau khi mất mạng, hãy cân nhắc đưa vào WorkManager
và ForegroundServices
.
Mã ví dụ để thiết lập quá trình tải xuống bằng Cronet như sau, được lấy từ lớp học lập trình về 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();
});
}
Tài nguyên
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-26 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 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)"]]