কন্টেন্টপ্রোভাইডার-প্রদত্ত ফাইলের নাম ভুলভাবে বিশ্বাস করা

OWASP বিভাগ: MASVS-CODE: কোডের গুণমান

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

FileProvider , যা ContentProvider- এর একটি সাবক্লাস, এর উদ্দেশ্য হলো একটি অ্যাপ্লিকেশন ("সার্ভার অ্যাপ্লিকেশন") থেকে অন্য একটি অ্যাপ্লিকেশন ("ক্লায়েন্ট অ্যাপ্লিকেশন")-এর সাথে ফাইল শেয়ার করার জন্য একটি নিরাপদ পদ্ধতি প্রদান করা। তবে, যদি ক্লায়েন্ট অ্যাপ্লিকেশনটি সার্ভার অ্যাপ্লিকেশন দ্বারা প্রদত্ত ফাইলের নামটি সঠিকভাবে পরিচালনা না করে, তাহলে একজন আক্রমণকারী-নিয়ন্ত্রিত সার্ভার অ্যাপ্লিকেশন ক্লায়েন্ট অ্যাপ্লিকেশনের অ্যাপ-নির্দিষ্ট স্টোরেজে থাকা ফাইলগুলিকে ওভাররাইট করার জন্য তার নিজস্ব ক্ষতিকারক FileProvider প্রয়োগ করতে সক্ষম হতে পারে।

প্রভাব

যদি কোনো আক্রমণকারী কোনো অ্যাপ্লিকেশনের ফাইল ওভাররাইট করতে পারে, তবে এর ফলে ক্ষতিকর কোড কার্যকর হতে পারে (অ্যাপ্লিকেশনটির কোড ওভাররাইট করার মাধ্যমে), অথবা অ্যাপ্লিকেশনটির আচরণ অন্যভাবে পরিবর্তন করা সম্ভব হতে পারে (উদাহরণস্বরূপ, অ্যাপ্লিকেশনটির শেয়ার্ড প্রেফারেন্স বা অন্যান্য কনফিগারেশন ফাইল ওভাররাইট করার মাধ্যমে)।

প্রশমন

ব্যবহারকারীর দেওয়া তথ্য বিশ্বাস করবেন না।

ফাইল সিস্টেম কল ব্যবহার করার সময় ব্যবহারকারীর ইনপুট ছাড়াই কাজ করতে পছন্দ করুন, এবং প্রাপ্ত ফাইলটি স্টোরেজে লেখার সময় একটি অনন্য ফাইলের নাম তৈরি করুন।

অন্য কথায়: ক্লায়েন্ট অ্যাপ্লিকেশনটি যখন প্রাপ্ত ফাইলটি স্টোরেজে লেখে, তখন তার উচিত সার্ভার অ্যাপ্লিকেশন দ্বারা প্রদত্ত ফাইলের নামটি উপেক্ষা করা এবং তার পরিবর্তে ফাইলের নাম হিসাবে নিজস্ব অভ্যন্তরীণভাবে তৈরি অনন্য শনাক্তকারী ব্যবহার করা।

এই উদাহরণটি https://developer.android.com/training/secure-file-sharing/request-file -এ পাওয়া কোডের উপর ভিত্তি করে তৈরি করা হয়েছে:

কোটলিন

// Code in
// https://developer.android.com/training/secure-file-sharing/request-file#OpenFile
// used to obtain file descriptor (fd)

try {
    val inputStream = FileInputStream(fd)
    val tempFile = File.createTempFile("temp", null, cacheDir)
    val outputStream = FileOutputStream(tempFile)
    val buf = ByteArray(1024)
    var len: Int
    len = inputStream.read(buf)
    while (len > 0) {
        if (len != -1) {
            outputStream.write(buf, 0, len)
            len = inputStream.read(buf)
        }
    }
    inputStream.close()
    outputStream.close()
} catch (e: IOException) {
    e.printStackTrace()
    Log.e("MainActivity", "File copy error.")
    return
}

জাভা

// Code in
// https://developer.android.com/training/secure-file-sharing/request-file#OpenFile
// used to obtain file descriptor (fd)

FileInputStream inputStream = new FileInputStream(fd);

// Create a temporary file
File tempFile = File.createTempFile("temp", null, getCacheDir());

// Copy the contents of the file to the temporary file
try {
    OutputStream outputStream = new FileOutputStream(tempFile))
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) > 0) {
        outputStream.write(buffer, 0, length);
    }
} catch (IOException e) {
    e.printStackTrace();
    Log.e("MainActivity", "File copy error.");
    return;
}

প্রদত্ত ফাইলের নামগুলি স্যানিটাইজ করুন

প্রাপ্ত ফাইলটি স্টোরেজে লেখার সময় প্রদত্ত ফাইলের নামটি পরিমার্জন করুন।

এই প্রতিকার ব্যবস্থাটি পূর্ববর্তী প্রতিকার ব্যবস্থার চেয়ে কম কাম্য, কারণ সমস্ত সম্ভাব্য পরিস্থিতি সামাল দেওয়া কঠিন হতে পারে। তথাপি: যদি একটি অনন্য ফাইলের নাম তৈরি করা বাস্তবসম্মত না হয়, তবে ক্লায়েন্ট অ্যাপ্লিকেশনটির উচিত প্রদত্ত ফাইলের নামটি পরিমার্জন করা। পরিমার্জনের মধ্যে অন্তর্ভুক্ত রয়েছে:

  • ফাইলের নামে পাথ ট্র্যাভার্সাল অক্ষরগুলি স্যানিটাইজ করা
  • কোনো পাথ ট্র্যাভার্সাল নেই তা নিশ্চিত করতে ক্যানোনিকালাইজেশন করা হচ্ছে।

এই উদাহরণ কোডটি ফাইলের তথ্য পুনরুদ্ধারের নির্দেশনার উপর ভিত্তি করে তৈরি করা হয়েছে:

কোটলিন

protected fun sanitizeFilename(displayName: String): String {
    val badCharacters = arrayOf("..", "/")
    val segments = displayName.split("/")
    var fileName = segments[segments.size - 1]
    for (suspString in badCharacters) {
        fileName = fileName.replace(suspString, "_")
    }
    return fileName
}

val displayName = returnCursor.getString(nameIndex)
val fileName = sanitizeFilename(displayName)
val filePath = File(context.filesDir, fileName).path

// saferOpenFile defined in Android developer documentation
val outputFile = saferOpenFile(filePath, context.filesDir.canonicalPath)

// fd obtained using Requesting a shared file from Android developer
// documentation

val inputStream = FileInputStream(fd)

// Copy the contents of the file to the new file
try {
    val outputStream = FileOutputStream(outputFile)
    val buffer = ByteArray(1024)
    var length: Int
    while (inputStream.read(buffer).also { length = it } > 0) {
        outputStream.write(buffer, 0, length)
    }
} catch (e: IOException) {
    // Handle exception
}

জাভা

protected String sanitizeFilename(String displayName) {
    String[] badCharacters = new String[] { "..", "/" };
    String[] segments = displayName.split("/");
    String fileName = segments[segments.length - 1];
    for (String suspString : badCharacters) {
        fileName = fileName.replace(suspString, "_");
    }
    return fileName;
}

String displayName = returnCursor.getString(nameIndex);
String fileName = sanitizeFilename(displayName);
String filePath = new File(context.getFilesDir(), fileName).getPath();

// saferOpenFile defined in Android developer documentation

File outputFile = saferOpenFile(filePath,
    context.getFilesDir().getCanonicalPath());

// fd obtained using Requesting a shared file from Android developer
// documentation

FileInputStream inputStream = new FileInputStream(fd);

// Copy the contents of the file to the new file
try {
    OutputStream outputStream = new FileOutputStream(outputFile))
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) > 0) {
        outputStream.write(buffer, 0, length);
    }
} catch (IOException e) {
    // Handle exception
}

অবদানকারী: মাইক্রোসফট থ্রেট ইন্টেলিজেন্সের দিমিত্রিওস ভালসামারাস এবং মাইকেল পেক

সম্পদ