فئة OWASP: MASVS-STORAGE: مساحة التخزين
نظرة عامة
تتعلّق ثغرة Zip Path Traversal الأمنية، المعروفة أيضًا باسم ZipSlip، بالتعامل مع الأرشيفات المضغوطة. في هذه الصفحة، نوضّح هذه الثغرة الأمنية باستخدام تنسيق ZIP كمثال، ولكن يمكن أن تنشأ مشاكل مشابهة في المكتبات التي تتعامل مع تنسيقات أخرى، مثل TAR أو RAR أو 7z.
السبب الأساسي لهذه المشكلة هو أنّه داخل أرشيفات ZIP، يتم تخزين كل ملف مضغوط باسم مؤهَّل بالكامل، ما يسمح باستخدام رموز خاصة مثل الشرطات المائلة والنقاط. لا تتحقّق المكتبة التلقائية من حزمة java.util.zip
من أسماء إدخالات الأرشيف بحثًا عن أحرف مسح الدليل (../
)، لذا يجب توخّي الحذر عند ربط الاسم المستخرَج من الأرشيف بمسار الدليل المستهدَف.
من المهم جدًا التحقّق من صحة أي مقتطفات رمز أو مكتبات لاستخراج ملفات ZIP من مصادر خارجية. العديد من هذه المكتبات معرَّضة لثغرات مسح مسار ملفات Zip.
التأثير
يمكن استخدام ثغرة مسح مسار ملفات Zip لتحقيق الكتابة فوق الملفات العشوائية. واستنادًا إلى الظروف، قد يختلف التأثير، ولكن في العديد من الحالات، يمكن أن يؤدي هذا الثغرة الأمنية إلى مشاكل أمنية كبيرة، مثل تنفيذ التعليمات البرمجية.
إجراءات التخفيف
للتخفيف من هذه المشكلة، يجب التأكّد دائمًا من أنّ المسار المستهدف هو عنصر فرعي من دليل الوجهة قبل استخراج كل إدخال. يفترض الرمز البرمجي أدناه أنّ دليل الوجهة آمن، أي أنّ تطبيقك وحده يمكنه الكتابة فيه وليس تحت سيطرة المهاجم، وإلا قد يكون تطبيقك عرضة لثغرات أمنية أخرى، مثل هجمات الروابط الرمزية.
Kotlin
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
}
}
Java
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;
}
لتجنُّب الكتابة فوق الملفات الحالية عن طريق الخطأ، يجب أيضًا التأكّد من أنّ دليل الوجهة فارغ قبل بدء عملية الاستخراج. وإلا فإنّك تخاطر باحتمالية تعطُّل التطبيق، أو في الحالات القصوى، اختراق التطبيق.
Kotlin
@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)
// ...
}
}
}
Java
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);
…
}
}
}
المراجع
أفلام مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما تكون JavaScript غير مفعّلة
- ثغرة Path traversal