অনিরাপদ ডাউনলোড ম্যানেজার

OWASP বিভাগ: MASVS-নেটওয়ার্ক: নেটওয়ার্ক যোগাযোগ

সংক্ষিপ্ত বিবরণ

ডাউনলোডম্যানেজার হলো এপিআই লেভেল ৯-এ প্রবর্তিত একটি সিস্টেম সার্ভিস। এটি দীর্ঘ সময় ধরে চলা এইচটিটিপি ডাউনলোডগুলো পরিচালনা করে এবং অ্যাপ্লিকেশনগুলোকে ব্যাকগ্রাউন্ড টাস্ক হিসেবে ফাইল ডাউনলোড করার সুযোগ দেয়। এর এপিআই এইচটিটিপি ইন্টারঅ্যাকশনগুলো পরিচালনা করে এবং ব্যর্থতার পর, অথবা কানেক্টিভিটি পরিবর্তন ও সিস্টেম রিবুটের পরেও ডাউনলোড পুনরায় চেষ্টা করে।

ডাউনলোডম্যানেজারে নিরাপত্তা-সংক্রান্ত দুর্বলতা থাকায় অ্যান্ড্রয়েড অ্যাপ্লিকেশনে ডাউনলোড ব্যবস্থাপনার জন্য এটি একটি অনিরাপদ বিকল্প।

(1) ডাউনলোড প্রোভাইডারে CVE

২০১৮ সালে ডাউনলোড প্রোভাইডারে তিনটি CVE শনাক্ত ও প্যাচ করা হয়েছিল। প্রতিটির একটি সারসংক্ষেপ নিচে দেওয়া হলো ( প্রযুক্তিগত বিবরণ দেখুন)।

  • ডাউনলোড প্রোভাইডারের অনুমতি বাইপাস – কোনো অনুমতি না থাকলে, একটি ক্ষতিকারক অ্যাপ ডাউনলোড প্রোভাইডার থেকে সমস্ত এন্ট্রি সংগ্রহ করতে পারে, যার মধ্যে ফাইলের নাম, বিবরণ, শিরোনাম, পাথ, ইউআরএল-এর মতো সম্ভাব্য সংবেদনশীল তথ্য থাকতে পারে, সেইসাথে ডাউনলোড করা সমস্ত ফাইলে সম্পূর্ণ রিড/রাইট (READ/WRITE) অনুমতিও অন্তর্ভুক্ত থাকতে পারে। একটি ক্ষতিকারক অ্যাপ ব্যাকগ্রাউন্ডে চলতে পারে, সমস্ত ডাউনলোড পর্যবেক্ষণ করতে পারে এবং দূর থেকে সেগুলোর বিষয়বস্তু ফাঁস করতে পারে, অথবা বৈধ অনুরোধকারী ফাইলগুলো অ্যাক্সেস করার আগেই সেগুলোকে তাৎক্ষণিকভাবে পরিবর্তন করতে পারে। এর ফলে ব্যবহারকারী মূল অ্যাপ্লিকেশনগুলো ব্যবহার করতে না পারার মতো সমস্যার সম্মুখীন হতে পারেন, যার মধ্যে আপডেট ডাউনলোড করতে না পারাও অন্তর্ভুক্ত।
  • ডাউনলোড প্রোভাইডার এসকিউএল ইনজেকশন – একটি এসকিউএল ইনজেকশন দুর্বলতার মাধ্যমে, অনুমতিবিহীন একটি ক্ষতিকারক অ্যাপ্লিকেশন ডাউনলোড প্রোভাইডার থেকে সমস্ত এন্ট্রি সংগ্রহ করতে পারে। এছাড়াও, android.permission.INTERNET মতো সীমিত অনুমতিযুক্ত অ্যাপ্লিকেশনগুলোও একটি ভিন্ন URI থেকে ডেটাবেসের সমস্ত কন্টেন্ট অ্যাক্সেস করতে পারে। ফাইলের নাম, বিবরণ, শিরোনাম, পাথ, ইউআরএল-এর মতো সম্ভাব্য সংবেদনশীল তথ্য সংগ্রহ করা যেতে পারে এবং অনুমতির উপর নির্ভর করে, ডাউনলোড করা কন্টেন্ট অ্যাক্সেস করাও সম্ভব হতে পারে।
  • ডাউনলোড প্রোভাইডার রিকোয়েস্ট হেডার থেকে তথ্য ফাঁসandroid.permission.INTERNET পারমিশন প্রাপ্ত একটি ক্ষতিকারক অ্যাপ্লিকেশন ডাউনলোড প্রোভাইডার রিকোয়েস্ট হেডার টেবিলের সমস্ত এন্ট্রি সংগ্রহ করতে পারে। এই হেডারগুলিতে অ্যান্ড্রয়েড ব্রাউজার বা গুগল ক্রোম সহ অন্যান্য অ্যাপ্লিকেশন থেকে শুরু করা যেকোনো ডাউনলোডের জন্য সেশন কুকি বা অথেনটিকেশন হেডারের মতো সংবেদনশীল তথ্য থাকতে পারে। এর ফলে একজন আক্রমণকারী এমন যেকোনো প্ল্যাটফর্মে ব্যবহারকারীর ছদ্মবেশ ধারণ করতে পারে, যেখান থেকে সংবেদনশীল ব্যবহারকারীর ডেটা সংগ্রহ করা হয়েছিল।

(2) বিপজ্জনক অনুমতি

DownloadManager in API levels lower than 29 requires dangerous permissions – android.permission.WRITE_EXTERNAL_STORAGE . For API level 29 and higher, android.permission.WRITE_EXTERNAL_STORAGE permissions are not required, but the URI must refer to a path within the directories owned by the application or a path within the top-level "Downloads" directory.

(3) Uri.parse() এর উপর নির্ভরতা

DownloadManager অনুরোধ করা ডাউনলোডের অবস্থান পার্স করার জন্য Uri.parse() মেথডের উপর নির্ভর করে। পারফরম্যান্সের স্বার্থে, Uri ক্লাস অবিশ্বস্ত ইনপুটের উপর খুব সামান্য বা কোনো যাচাইকরণ প্রয়োগ করে না।

প্রভাব

DownloadManager ব্যবহার করলে এক্সটার্নাল স্টোরেজে WRITE পারমিশনের অপব্যবহারের মাধ্যমে দুর্বলতা দেখা দিতে পারে। যেহেতু android.permission.WRITE_EXTERNAL_STORAGE পারমিশন এক্সটার্নাল স্টোরেজে ব্যাপক অ্যাক্সেস দেয়, তাই একজন আক্রমণকারীর পক্ষে গোপনে ফাইল ও ডাউনলোড পরিবর্তন করা, সম্ভাব্য ক্ষতিকারক অ্যাপ ইনস্টল করা, কোর অ্যাপগুলোকে পরিষেবা দেওয়া থেকে বিরত রাখা, অথবা অ্যাপ ক্র্যাশ করানো সম্ভব। ক্ষতিকারক ব্যক্তিরা Uri.parse()-এ পাঠানো তথ্য পরিবর্তন করে ব্যবহারকারীকে একটি ক্ষতিকারক ফাইল ডাউনলোড করতে বাধ্য করতে পারে।

প্রশমন

DownloadManager ব্যবহার করার পরিবর্তে, একটি HTTP ক্লায়েন্ট (যেমন Cronet), একটি প্রসেস শিডিউলার/ম্যানেজার এবং নেটওয়ার্ক সংযোগ বিচ্ছিন্ন হলে পুনরায় চেষ্টা নিশ্চিত করার একটি ব্যবস্থা ব্যবহার করে সরাসরি আপনার অ্যাপে ডাউনলোড সেট আপ করুন। লাইব্রেরিটির ডকুমেন্টেশনে একটি নমুনা অ্যাপের লিঙ্কের পাশাপাশি এটি কীভাবে প্রয়োগ করতে হয় তার নির্দেশাবলীও রয়েছে।

আপনার অ্যাপ্লিকেশনে যদি প্রসেস শিডিউলিং পরিচালনা করা, ব্যাকগ্রাউন্ডে ডাউনলোড চালানো, অথবা নেটওয়ার্ক সংযোগ বিচ্ছিন্ন হওয়ার পর ডাউনলোড পুনরায় চালু করার চেষ্টার মতো সক্ষমতার প্রয়োজন হয়, তাহলে WorkManager এবং ForegroundServices অন্তর্ভুক্ত করার কথা বিবেচনা করতে পারেন।

ক্রোনেট কোডল্যাব থেকে নেওয়া, ক্রোনেট ব্যবহার করে ডাউনলোড সেট আপ করার উদাহরণ কোড নিচে দেওয়া হলো।

কোটলিন

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

জাভা

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

সম্পদ