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

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

खास जानकारी

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

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

(1) डाउनलोड की सेवा देने वाली कंपनी में सीवीई

साल 2018 में, DownloadProvider में तीन सीवीई मिले और उन्हें ठीक कर दिया गया. इनके बारे में खास जानकारी यहां दी गई है (तकनीकी जानकारी देखें).

  • डाउनलोड की सुविधा देने वाली कंपनी से अनुमति को बायपास करना – कोई अनुमति दिए बिना भी, कोई नुकसान पहुंचाने वाला ऐप्लिकेशन, डाउनलोड की सुविधा देने वाली कंपनी से सभी एंट्री हासिल कर सकता है. इन एंट्री में, फ़ाइल के नाम, ब्यौरा, टाइटल, पाथ, यूआरएल जैसी संवेदनशील जानकारी शामिल हो सकती है. साथ ही, डाउनलोड की गई सभी फ़ाइलों को पूरी तरह से पढ़ने/लिखने की अनुमतियां भी हासिल की जा सकती हैं. नुकसान पहुंचाने वाला ऐप्लिकेशन, बैकग्राउंड में चल सकता है. यह सभी डाउनलोड की निगरानी करता है और उनके कॉन्टेंट को रिमोट से लीक करता है. इसके अलावा, यह ऐप्लिकेशन, फ़ाइलों को ऐक्सेस करने से पहले ही उनमें बदलाव कर सकता है. इससे उपयोगकर्ता को मुख्य ऐप्लिकेशन के लिए, सेवा अस्वीकार होने की समस्या आ सकती है. साथ ही, अपडेट डाउनलोड करने में भी समस्या आ सकती है.
  • प्रोवाइडर एसक्यूएल इंजेक्शन को डाउनलोड करें – एसक्यूएल इंजेक्शन के दौरान जोखिम की आशंका की वजह से, नुकसान पहुंचाने वाला ऐसा ऐप्लिकेशन जिसके पास अनुमति नहीं है, वह डाउनलोड की सेवा देने वाली कंपनी की सभी एंट्री वापस पा सकता है. साथ ही, सीमित अनुमतियों वाले ऐप्लिकेशन, जैसे कि android.permission.INTERNET, किसी दूसरे यूआरआई से भी डेटाबेस का सारा कॉन्टेंट ऐक्सेस कर सकते हैं. फ़ाइल के नाम, ब्यौरे, टाइटल, पाथ, यूआरएल जैसी संवेदनशील जानकारी को वापस पाया जा सकता है. साथ ही, अनुमतियों के आधार पर, डाउनलोड किए गए कॉन्टेंट को भी ऐक्सेस किया जा सकता है.
  • डाउनलोड की सेवा देने वाली कंपनी के अनुरोध के हेडर की जानकारी ज़ाहिर करनाandroid.permission.INTERNET अनुमति वाला कोई नुकसान पहुंचाने वाला ऐप्लिकेशन, डाउनलोड की सेवा देने वाली कंपनी के अनुरोध के हेडर की टेबल से सभी एंट्री हासिल कर सकता है. इन हेडर में संवेदनशील जानकारी शामिल हो सकती है. जैसे, Android ब्राउज़र या 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 कोडलैब से लिया गया है.

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

संसाधन