Niebezpieczny menedżer pobierania
Zadbaj o dobrą organizację dzięki kolekcji
Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.
Kategoria OWASP: MASVS-NETWORK: komunikacja sieciowa
Omówienie
DownloadManager to usługa systemowa wprowadzona w interfejsie API poziomu 9. Obsługuje długotrwałe pobieranie przez HTTP i pozwala aplikacjom pobierać pliki jako zadania w tle. Jego interfejs API obsługuje interakcje HTTP i ponawia próby pobrania w przypadku awarii, zmian połączenia i ponownego uruchomienia systemu.
DownloadManager ma luki w zabezpieczeniach związane z bezpieczeństwem, przez co nie jest to bezpieczne rozwiązanie do zarządzania pobieraniem w aplikacjach na Androida.
(1) CVE w dostawcy pobierania
W 2018 r. w DownloadProvider znaleziono 3 luki CVE, które zostały załatane. Poniżej znajdziesz podsumowanie każdego z nich (patrz szczegóły techniczne).
- Pominięcie uprawnień dostawcy – bez przyznanych uprawnień złośliwa aplikacja może pobrać wszystkie wpisy od dostawcy pobierania, które mogą zawierać potencjalnie poufne informacje, takie jak nazwy plików, opisy, tytuły, ścieżki, adresy URL, a także pełne uprawnienia do ODCZYTU/ZAPISU wszystkich pobranych plików. Złośliwa aplikacja może działać w tle, monitorując wszystkie pliki do pobrania i zdalnie udostępniając ich zawartość lub modyfikując je na bieżąco, zanim uzyska do nich dostęp żądający użytkownik. Może to spowodować odmowę usługi dla użytkownika w przypadku podstawowych aplikacji, w tym brak możliwości pobrania aktualizacji.
- Wstrzyknięcie kodu SQL na poziomie dostawcy pobierania – dzięki luki w zabezpieczeniach umożliwiającej wstrzyknięcie kodu SQL złośliwa aplikacja bez uprawnień może pobrać wszystkie wpisy od dostawcy pobierania. Aplikacje z ograniczonymi uprawnieniami, np.
android.permission.INTERNET
, mogą też uzyskiwać dostęp do wszystkich treści bazy danych za pomocą innego identyfikatora URI. Mogą też zostać pobrane informacje poufne, takie jak nazwy plików, opisy, tytuły, ścieżki i adresy URL. W zależności od uprawnień może być również możliwy dostęp do pobranych treści.
- Ujawnienie informacji z nagłówków żądań dostawcy treści do pobrania – złośliwa aplikacja z przyznanym uprawnieniem
android.permission.INTERNET
może pobrać wszystkie wpisy z tabeli nagłówków żądań dostawcy treści do pobrania. Te nagłówki mogą zawierać informacje poufne, takie jak pliki cookie sesji lub nagłówki uwierzytelniania, w przypadku każdego pobierania rozpoczętego w przeglądarce Androida lub przeglądarce Google Chrome. Może to umożliwić atakującemu podszywanie się pod użytkownika na dowolnej platformie, z której pozyskano dane wrażliwe.
(2) Niebezpieczne uprawnienia
Interfejs DownloadManager na poziomie niższym niż 29 wymaga uprawnień niebezpiecznych –android.permission.WRITE_EXTERNAL_STORAGE
. W przypadku interfejsu API na poziomie 29 lub wyższym uprawnienia android.permission.WRITE_EXTERNAL_STORAGE
nie są wymagane, ale identyfikator URI musi wskazywać ścieżkę w katalogach należących do aplikacji lub ścieżkę w katalogu najwyższego poziomu „Pobrane”.
(3) Uzależnienie od Uri.parse()
Menedżer pobierania korzysta z metody Uri.parse()
, aby analizować lokalizację żądanego pliku. Ze względu na wydajność klasa Uri
stosuje niewiele lub wcale nie stosuje weryfikacji w przypadku niesprawdzonych danych wejściowych.
Wpływ
Korzystanie z klasy DownloadManager może prowadzić do luk w zabezpieczeniach, ponieważ umożliwia wykorzystywanie uprawnień do zapisu w pamięci zewnętrznej. Uprawnienia android.permission.WRITE_EXTERNAL_STORAGE dają szeroki dostęp do pamięci zewnętrznej, dlatego atakujący może dyskretnie modyfikować i pobierać pliki, instalować potencjalnie szkodliwe aplikacje, odrzucać usługi oraz powodować awarie aplikacji. Osoby o złośliwych zamiarach mogą też manipulować tym, co jest wysyłane do funkcji Uri.parse(), aby zmusić użytkownika do pobrania szkodliwego pliku.
Środki zaradcze
Zamiast używać DownloadManager, skonfiguruj pobieranie bezpośrednio w aplikacji, korzystając z klienta HTTP (np. Cronet), harmonogramu procesów lub menedżera procesów oraz sposobu zapewnienia ponownych prób w przypadku utraty połączenia z internetem. Dokumentacja biblioteki zawiera link do przykładowej aplikacji oraz instrukcje dotyczące jej implementacji.
Jeśli Twoja aplikacja wymaga możliwości zarządzania harmonogramem procesów, uruchamiania pobierania w tle lub ponownego nawiązywania połączenia po utracie połączenia z internetem, rozważ dodanie WorkManager
i ForegroundServices
.
Oto przykładowy kod konfiguracji pobierania za pomocą Cronet (wycięty w 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();
});
}
Materiały
Treść strony i umieszczone na niej fragmenty kodu podlegają licencjom opisanym w Licencji na treści. Java i OpenJDK są znakami towarowymi lub zastrzeżonymi znakami towarowymi należącymi do firmy Oracle lub jej podmiotów stowarzyszonych.
Ostatnia aktualizacja: 2025-07-26 UTC.
[[["Łatwo zrozumieć","easyToUnderstand","thumb-up"],["Rozwiązało to mój problem","solvedMyProblem","thumb-up"],["Inne","otherUp","thumb-up"]],[["Brak potrzebnych mi informacji","missingTheInformationINeed","thumb-down"],["Zbyt skomplikowane / zbyt wiele czynności do wykonania","tooComplicatedTooManySteps","thumb-down"],["Nieaktualne treści","outOfDate","thumb-down"],["Problem z tłumaczeniem","translationIssue","thumb-down"],["Problem z przykładami/kodem","samplesCodeIssue","thumb-down"],["Inne","otherDown","thumb-down"]],["Ostatnia aktualizacja: 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)"]]