डाउनलोड मैनेजर असुरक्षित है

OWASP कैटगरी: MASVS-NETWORK: नेटवर्क से कम्यूनिकेशन

खास जानकारी

DownloadManager, एपीआई लेवल 9 में शुरू की गई एक सिस्टम सेवा है. यह एचटीटीपी के ज़रिए लंबे समय तक चलने वाले डाउनलोड को मैनेज करती है. साथ ही, ऐप्लिकेशन को बैकग्राउंड में फ़ाइलें डाउनलोड करने की अनुमति देती है. इसका एपीआई, एचटीटीपी इंटरैक्शन को मैनेज करता है. साथ ही, डाउनलोड में गड़बड़ी होने, कनेक्टिविटी में बदलाव होने या सिस्टम रीबूट होने पर, डाउनलोड को फिर से शुरू करता है.

DownloadManager में सुरक्षा से जुड़ी कमज़ोरियां हैं. इस वजह से, Android ऐप्लिकेशन में डाउनलोड मैनेज करने के लिए, यह सुरक्षित विकल्प नहीं है.

(1) Download Provider में मौजूद सीवीई

साल 2018 में, Download Provider में तीन सीवीई मिले थे. इन्हें ठीक कर दिया गया है. इनमें से हर सीवीई की खास जानकारी यहां दी गई है. ज़्यादा जानकारी के लिए, तकनीकी जानकारी देखें.

  • Download Provider की अनुमति को बाईपास करना – अनुमति न मिलने पर भी, कोई नुकसान पहुंचाने वाला ऐप्लिकेशन, Download Provider से सभी एंट्री वापस पा सकता है. इनमें फ़ाइल के नाम, ब्यौरे, टाइटल, पाथ, यूआरएल जैसी संवेदनशील जानकारी के साथ-साथ, डाउनलोड की गई सभी फ़ाइलों के लिए, READ/WRITE की पूरी अनुमतियां शामिल हो सकती हैं. कोई नुकसान पहुंचाने वाला ऐप्लिकेशन, बैकग्राउंड में चल सकता है. साथ ही, सभी डाउनलोड पर नज़र रख सकता है और उनका कॉन्टेंट, दूर से ही लीक कर सकता है. इसके अलावा, असली अनुरोध करने वाले व्यक्ति के ऐक्सेस करने से पहले, फ़ाइलों में बदलाव कर सकता है. इससे उपयोगकर्ता के लिए, मुख्य ऐप्लिकेशन में सेवा से जुड़ी गड़बड़ी हो सकती है. इसमें अपडेट डाउनलोड न कर पाने की समस्या भी शामिल है.
  • Download Provider में एसक्यूएल इंजेक्शन – एसक्यूएल इंजेक्शन की गड़बड़ी की वजह से, बिना अनुमति वाला कोई नुकसान पहुंचाने वाला ऐप्लिकेशन, Download Provider से सभी एंट्री वापस पा सकता है. इसके अलावा, सीमित अनुमतियों वाले ऐप्लिकेशन, जैसे कि android.permission.INTERNET, किसी दूसरे यूआरआई से भी डेटाबेस के सभी कॉन्टेंट को ऐक्सेस कर सकते हैं. फ़ाइल के नाम, ब्यौरे, टाइटल, पाथ, यूआरएल जैसी संवेदनशील जानकारी वापस पाई जा सकती है. साथ ही, अनुमतियों के आधार पर, डाउनलोड किए गए कॉन्टेंट को ऐक्सेस किया जा सकता है.
  • Download Provider के अनुरोध के हेडर की जानकारी का खुलासा – `android.permission.INTERNET` की अनुमति वाला कोई नुकसान पहुंचाने वाला ऐप्लिकेशन, Download Provider के अनुरोध के हेडर की टेबल से सभी एंट्री वापस पा सकता है.android.permission.INTERNET इन हेडर में संवेदनशील जानकारी शामिल हो सकती है. जैसे, Android Browser या Google Chrome से शुरू किए गए किसी भी डाउनलोड के लिए, सेशन कुकी या पुष्टि करने वाले हेडर. इसके अलावा, अन्य ऐप्लिकेशन के लिए भी यह जानकारी शामिल हो सकती है. इससे हमलावर, किसी भी ऐसे प्लैटफ़ॉर्म पर उपयोगकर्ता के तौर पर काम कर सकता है जिससे उपयोगकर्ता का संवेदनशील डेटा मिला हो.

(2) खतरनाक अनुमतियां

एपीआई लेवल 29 से कम वाले DownloadManager के लिए, खतरनाक अनुमतियों – android.permission.WRITE_EXTERNAL_STORAGE की ज़रूरत होती है. एपीआई लेवल 29 और उससे ज़्यादा वाले वर्शन के लिए, android.permission.WRITE_EXTERNAL_STORAGE अनुमतियों की ज़रूरत नहीं होती. हालांकि, यूआरआई को ऐप्लिकेशन के मालिकाना हक वाली डायरेक्ट्री में मौजूद किसी पाथ या टॉप-लेवल "डाउनलोड" डायरेक्ट्री में मौजूद किसी पाथ पर ले जाना ज़रूरी है.

(3) Uri.parse() **पर निर्भरता**

DownloadManager, अनुरोध किए गए डाउनलोड की लोकेशन को पार्स करने के लिए, Uri.parse() तरीके पर निर्भर करता है. परफ़ॉर्मेंस को बेहतर बनाने के लिए, Uri क्लास, भरोसेमंद न होने वाले इनपुट पर बहुत कम या कोई पुष्टि नहीं करती.

असर

DownloadManager का इस्तेमाल करने से, बाहरी स्टोरेज में WRITE की अनुमतियों का गलत इस्तेमाल करके, गड़बड़ियां हो सकती हैं. android.permission.WRITE_EXTERNAL_STORAGE की अनुमतियों से, बाहरी स्टोरेज को बड़े पैमाने पर ऐक्सेस किया जा सकता है. इसलिए, हमलावर चुपके से फ़ाइलों और डाउनलोड में बदलाव कर सकता है, नुकसान पहुंचाने वाले ऐप्लिकेशन इंस्टॉल कर सकता है, मुख्य ऐप्लिकेशन के लिए सेवा से जुड़ी गड़बड़ी कर सकता है या ऐप्लिकेशन को क्रैश कर सकता है. नुकसान पहुंचाने वाले लोग, Uri.parse() को भेजे गए डेटा में भी बदलाव कर सकते हैं, ताकि उपयोगकर्ता कोई नुकसान पहुंचाने वाली फ़ाइल डाउनलोड कर ले.

कम करने के तरीके

DownloadManager का इस्तेमाल करने के बजाय, अपने ऐप्लिकेशन में सीधे तौर पर डाउनलोड सेट अप करें. इसके लिए, एचटीटीपी क्लाइंट (जैसे, Cronet), प्रोसेस शेड्यूलर/मैनेजर, और नेटवर्क में गड़बड़ी होने पर, डाउनलोड को फिर से शुरू करने की सुविधा का इस्तेमाल करें. लाइब्रेरी के दस्तावेज़ में, सैंपल ऐप्लिकेशन का लिंक और इसे लागू करने के तरीके के बारे में निर्देश शामिल हैं.

अगर आपके ऐप्लिकेशन को प्रोसेस शेड्यूलिंग मैनेज करने, बैकग्राउंड में डाउनलोड करने या नेटवर्क में गड़बड़ी होने के बाद, डाउनलोड को फिर से शुरू करने की ज़रूरत है, तो WorkManager और ForegroundServices को शामिल करने पर विचार करें.

Cronet का इस्तेमाल करके, डाउनलोड सेट अप करने के लिए कोड का उदाहरण यहां दिया गया है. यह उदाहरण, Cronet codelab से लिया गया है.

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();
    });
}

संसाधन