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