OWASP বিভাগ: MASVS-STORAGE: স্টোরেজ
সংক্ষিপ্ত বিবরণ
জিপ পাথ ট্র্যাভার্সাল দুর্বলতা, যা জিপস্লিপ (ZipSlip) নামেও পরিচিত, সংকুচিত আর্কাইভ পরিচালনার সাথে সম্পর্কিত। এই পৃষ্ঠায়, আমরা উদাহরণ হিসেবে জিপ (ZIP) ফরম্যাট ব্যবহার করে এই দুর্বলতাটি প্রদর্শন করছি, কিন্তু টার (TAR), রার (RAR), বা সেভেনজেড (7z)-এর মতো অন্যান্য ফরম্যাট পরিচালনাকারী লাইব্রেরিতেও একই ধরনের সমস্যা দেখা দিতে পারে।
এই সমস্যার মূল কারণ হলো, ZIP আর্কাইভের ভেতরে প্রতিটি প্যাক করা ফাইল একটি সম্পূর্ণ নাম (fully qualified name) সহ সংরক্ষিত থাকে, যেখানে স্ল্যাশ এবং ডটের মতো বিশেষ অক্ষর ব্যবহারের সুযোগ থাকে। java.util.zip প্যাকেজের ডিফল্ট লাইব্রেরিটি আর্কাইভ এন্ট্রির নামগুলোতে ডিরেক্টরি ট্র্যাভার্সাল ক্যারেক্টার ( ../ ) আছে কিনা তা পরীক্ষা করে না, তাই আর্কাইভ থেকে পাওয়া নামের সাথে নির্দিষ্ট ডিরেক্টরি পাথ যুক্ত করার সময় বিশেষ সতর্কতা অবলম্বন করতে হবে।
বাহ্যিক উৎস থেকে প্রাপ্ত যেকোনো ZIP ফাইল এক্সট্র্যাক্টকারী কোড স্নিপেট বা লাইব্রেরি যাচাই করা অত্যন্ত গুরুত্বপূর্ণ। এই ধরনের অনেক লাইব্রেরি জিপ পাথ ট্র্যাভার্সালের (Zip Path Traversals) ঝুঁকিতে থাকে।
প্রভাব
জিপ পাথ ট্র্যাভার্সাল দুর্বলতা ব্যবহার করে যথেচ্ছভাবে ফাইল ওভাররাইট করা যায়। পরিস্থিতিভেদে এর প্রভাব ভিন্ন হতে পারে, কিন্তু অনেক ক্ষেত্রে এই দুর্বলতা কোড এক্সিকিউশনের মতো গুরুতর নিরাপত্তা সমস্যার কারণ হতে পারে।
প্রশমন
এই সমস্যাটি সমাধান করার জন্য, প্রতিটি এন্ট্রি এক্সট্র্যাক্ট করার আগে, আপনার সর্বদা যাচাই করে নেওয়া উচিত যে টার্গেট পাথটি গন্তব্য ডিরেক্টরির একটি চাইল্ড (child) কিনা। নীচের কোডটি ধরে নেয় যে গন্তব্য ডিরেক্টরিটি নিরাপদ – এটি শুধুমাত্র আপনার অ্যাপ দ্বারা রাইটেবল এবং কোনো আক্রমণকারীর নিয়ন্ত্রণে নেই – অন্যথায় আপনার অ্যাপ সিমলিঙ্ক অ্যাটাকের মতো অন্যান্য দুর্বলতার ঝুঁকিতে পড়তে পারে।
কোটলিন
companion object {
@Throws(IOException::class)
fun newFile(targetPath: File, zipEntry: ZipEntry): File {
val name: String = zipEntry.name
val f = File(targetPath, name)
val canonicalPath = f.canonicalPath
if (!canonicalPath.startsWith(
targetPath.canonicalPath + File.separator)) {
throw ZipException("Illegal name: $name")
}
return f
}
}
জাভা
public static File newFile(File targetPath, ZipEntry zipEntry) throws IOException {
String name = zipEntry.getName();
File f = new File(targetPath, name);
String canonicalPath = f.getCanonicalPath();
if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {
throw new ZipException("Illegal name: " + name);
}
return f;
}
ভুলবশত বিদ্যমান ফাইলগুলো ওভাররাইট হওয়া এড়াতে, এক্সট্র্যাকশন প্রক্রিয়া শুরু করার আগে গন্তব্য ডিরেক্টরিটি খালি আছে কিনা তা নিশ্চিত করা উচিত। অন্যথায়, অ্যাপ ক্র্যাশ হওয়ার ঝুঁকি থাকে, অথবা চরম ক্ষেত্রে, অ্যাপ্লিকেশনটির নিরাপত্তা বিঘ্নিত হতে পারে।
কোটলিন
@Throws(IOException::class)
fun unzip(inputStream: InputStream?, destinationDir: File) {
if (!destinationDir.isDirectory) {
throw IOException("Destination is not a directory.")
}
val files = destinationDir.list()
if (files != null && files.isNotEmpty()) {
throw IOException("Destination directory is not empty.")
}
ZipInputStream(inputStream).use { zipInputStream ->
var zipEntry: ZipEntry
while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
val targetFile = File(destinationDir, zipEntry.name)
// ...
}
}
}
জাভা
void unzip(final InputStream inputStream, File destinationDir)
throws IOException {
if(!destinationDir.isDirectory()) {
throw IOException("Destination is not a directory.");
}
String[] files = destinationDir.list();
if(files != null && files.length != 0) {
throw IOException("Destination directory is not empty.");
}
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
final File targetFile = new File(destinationDir, zipEntry);
…
}
}
}
সম্পদ
{% হুবহু %}আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলেও লিঙ্কের লেখা প্রদর্শিত হয়।
- পথ অতিক্রমণ