Pengelola Download Tidak Aman

Kategori OWASP: MASVS-NETWORK: Komunikasi Jaringan

Ringkasan

DownloadManager adalah layanan sistem yang diperkenalkan di API level 9. Library ini menangani download HTTP yang berjalan lama dan memungkinkan aplikasi mendownload file sebagai tugas latar belakang. API-nya menangani interaksi HTTP dan mencoba ulang download setelah terjadi kegagalan atau setelah terjadi perubahan konektivitas dan dimulai ulang sistem.

DownloadManager memiliki kelemahan yang relevan terkait keamanan yang menjadikannya pilihan yang tidak aman untuk mengelola download di aplikasi Android.

(1) CVE di Penyedia Download

Pada tahun 2018, tiga CVE ditemukan dan ditambal di Download Provider. Berikut ringkasan masing-masing (lihat detail teknis).

  • Pembobolan Izin Penyedia Download – Tanpa izin yang diberikan, aplikasi berbahaya dapat mengambil semua entri dari Penyedia Download, yang dapat menyertakan informasi yang berpotensi sensitif seperti nama file, deskripsi, judul, jalur, URL, serta izin BACA/TULIS penuh ke semua file yang didownload. Aplikasi berbahaya dapat berjalan di latar belakang, memantau semua download dan membocorkan kontennya dari jarak jauh, atau memodifikasi file dengan cepat sebelum diakses oleh pemohon yang sah. Hal ini dapat menyebabkan denial-of-service bagi pengguna untuk aplikasi inti, termasuk ketidakmampuan untuk mendownload update.
  • Injeksi SQL Penyedia Download – Melalui kerentanan injeksi SQL, aplikasi berbahaya tanpa izin dapat mengambil semua entri dari Penyedia Download. Selain itu, aplikasi dengan izin terbatas, seperti android.permission.INTERNET, juga dapat mengakses semua konten database dari URI yang berbeda. Informasi yang berpotensi sensitif seperti nama file, deskripsi, judul, jalur, URL dapat diambil, dan, bergantung pada izin, akses ke konten yang didownload juga dapat dilakukan.
  • Pengungkapan Informasi Header Permintaan Penyedia Download – Aplikasi malicious dengan izin android.permission.INTERNET yang diberikan dapat mengambil semua entri dari tabel header permintaan Penyedia Download. Header ini dapat mencakup informasi sensitif, seperti cookie sesi atau header autentikasi, untuk setiap download yang dimulai dari Browser Android atau Google Chrome, di antara aplikasi lainnya. Hal ini memungkinkan penyerang meniru identitas pengguna di platform apa pun tempat data pengguna sensitif diperoleh.

(2) Izin Berbahaya

DownloadManager di level API yang lebih rendah dari 29 memerlukan izin berbahaya – android.permission.WRITE_EXTERNAL_STORAGE. Untuk level API 29 dan yang lebih tinggi, izin android.permission.WRITE_EXTERNAL_STORAGE tidak diperlukan, tetapi URI harus merujuk pada jalur dalam direktori yang dimiliki oleh aplikasi atau jalur dalam direktori "Download" level atas.

(3) Ketergantungan pada Uri.parse()

DownloadManager mengandalkan metode Uri.parse() untuk mengurai lokasi download yang diminta. Demi performa, class Uri menerapkan sedikit atau tidak ada validasi pada input yang tidak tepercaya.

Dampak

Menggunakan DownloadManager dapat menyebabkan kerentanan melalui eksploitasi izin WRITE ke penyimpanan eksternal. Karena izin android.permission.WRITE_EXTERNAL_STORAGE memungkinkan akses luas ke penyimpanan eksternal, penyerang dapat mengubah file dan download secara diam-diam, menginstal aplikasi yang berpotensi membahayakan, menolak layanan ke aplikasi inti, atau menyebabkan aplikasi error. Pelaku berbahaya juga dapat memanipulasi apa yang dikirim ke Uri.parse() untuk menyebabkan pengguna mendownload file berbahaya.

Mitigasi

Daripada menggunakan DownloadManager, siapkan download langsung di aplikasi Anda menggunakan klien HTTP (seperti Cronet), penjadwal/pengelola proses, dan cara untuk memastikan percobaan ulang jika ada kehilangan jaringan. Dokumentasi library menyertakan link ke aplikasi contoh serta petunjuk tentang cara menerapkan library tersebut.

Jika aplikasi Anda memerlukan kemampuan untuk mengelola penjadwalan proses, menjalankan download di latar belakang, atau mencoba lagi membuat download setelah kehilangan jaringan, sebaiknya sertakan WorkManager dan ForegroundServices.

Contoh kode untuk menyiapkan download menggunakan Cronet adalah sebagai berikut, yang diambil dari 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();
    });
}

Referensi