مسح مسار ملفات Zip
تنظيم صفحاتك في مجموعات
يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.
فئة 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);
…
}
}
}
المراجع
أفلام مُقترَحة لك
يخضع كل من المحتوى وعيّنات التعليمات البرمجية في هذه الصفحة للتراخيص الموضحّة في ترخيص استخدام المحتوى. إنّ Java وOpenJDK هما علامتان تجاريتان مسجَّلتان لشركة Oracle و/أو الشركات التابعة لها.
تاريخ التعديل الأخير: 2025-07-27 (حسب التوقيت العالمي المتفَّق عليه)
[[["يسهُل فهم المحتوى.","easyToUnderstand","thumb-up"],["ساعَدني المحتوى في حلّ مشكلتي.","solvedMyProblem","thumb-up"],["غير ذلك","otherUp","thumb-up"]],[["لا يحتوي على المعلومات التي أحتاج إليها.","missingTheInformationINeed","thumb-down"],["الخطوات معقدة للغاية / كثيرة جدًا.","tooComplicatedTooManySteps","thumb-down"],["المحتوى قديم.","outOfDate","thumb-down"],["ثمة مشكلة في الترجمة.","translationIssue","thumb-down"],["مشكلة في العيّنات / التعليمات البرمجية","samplesCodeIssue","thumb-down"],["غير ذلك","otherDown","thumb-down"]],["تاريخ التعديل الأخير: 2025-07-27 (حسب التوقيت العالمي المتفَّق عليه)"],[],[],null,["# Zip Path Traversal\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-STORAGE: Storage](https://mas.owasp.org/MASVS/05-MASVS-STORAGE)\n\nOverview\n--------\n\nThe Zip Path Traversal vulnerability, also known as ZipSlip, is related to handling compressed archives. On this page, we demonstrate this vulnerability using the ZIP format as an example, but similar problems can arise in libraries handling other formats, like TAR, RAR, or 7z.\n\nThe underlying reason for this problem is that inside ZIP archives, each packed file is stored with a fully qualified name, which allows special characters such as slashes and dots. The default library from the `java.util.zip` package doesn't check the names of the archive entries for directory traversal characters (`../`), so special care must be taken when concatenating the name extracted from the archive with the targeted directory path.\n\nIt's very important to validate any ZIP-extracting code snippets or libraries from external sources. **Many such libraries are vulnerable to Zip Path Traversals.**\n\nImpact\n------\n\nThe Zip Path Traversal vulnerability can be used to achieve arbitrary file overwrite. Depending on conditions, the impact might vary, but in many cases this vulnerability can lead to major security issues such as code execution.\n\nMitigations\n-----------\n\nTo mitigate this issue, before extracting each entry, you should always verify that the target path is a child of the destination directory. The code below assumes that the destination directory is safe -- writable by your app only and not under attacker control -- otherwise your app could be prone to other vulnerabilities such as symlink attacks. \n\n### Kotlin\n\n companion object {\n @Throws(IOException::class)\n fun newFile(targetPath: File, zipEntry: ZipEntry): File {\n val name: String = zipEntry.name\n val f = File(targetPath, name)\n val canonicalPath = f.canonicalPath\n if (!canonicalPath.startsWith(\n targetPath.canonicalPath + File.separator)) {\n throw ZipException(\"Illegal name: $name\")\n }\n return f\n }\n }\n\n### Java\n\n public static File newFile(File targetPath, ZipEntry zipEntry) throws IOException {\n String name = zipEntry.getName();\n File f = new File(targetPath, name);\n String canonicalPath = f.getCanonicalPath();\n if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {\n throw new ZipException(\"Illegal name: \" + name);\n }\n return f;\n }\n\nTo avoid accidentally overwriting existing files, you should also make sure that the destination directory is empty before starting the extraction process. Otherwise you risk potential app crashes, or in extreme cases, an application compromise. \n\n### Kotlin\n\n @Throws(IOException::class)\n fun unzip(inputStream: InputStream?, destinationDir: File) {\n if (!destinationDir.isDirectory) {\n throw IOException(\"Destination is not a directory.\")\n }\n val files = destinationDir.list()\n if (files != null && files.isNotEmpty()) {\n throw IOException(\"Destination directory is not empty.\")\n }\n ZipInputStream(inputStream).use { zipInputStream -\u003e\n var zipEntry: ZipEntry\n while (zipInputStream.nextEntry.also { zipEntry = it } != null) {\n val targetFile = File(destinationDir, zipEntry.name)\n // ...\n }\n }\n }\n\n### Java\n\n void unzip(final InputStream inputStream, File destinationDir)\n throws IOException {\n if(!destinationDir.isDirectory()) { \n throw IOException(\"Destination is not a directory.\");\n }\n\n String[] files = destinationDir.list();\n if(files != null && files.length != 0) { \n throw IOException(\"Destination directory is not empty.\");\n }\n\n try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {\n ZipEntry zipEntry;\n while ((zipEntry = zipInputStream.getNextEntry()) != null) {\n final File targetFile = new File(destinationDir, zipEntry);\n ...\n }\n }\n }\n\nResources\n---------\n\n- [Zip Slip Vulnerability](https://snyk.io/research/zip-slip-vulnerability)\n\nRecommended for you\n-------------------\n\n- Note: link text is displayed when JavaScript is off\n- [Path traversal](/topic/security/risks/path-traversal)"]]