Truyền tải qua đường dẫn Zip
Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Danh mục OWASP: MASVS-STORAGE: Bộ nhớ
Tổng quan
Lỗ hổng bảo mật Truyền tải qua đường dẫn Zip (còn gọi là ZipSlip) liên quan đến việc xử lý các tệp lưu trữ nén. Trên trang này, chúng tôi minh hoạ lỗ hổng bảo mật đó bằng cách sử dụng định dạng ZIP làm ví dụ, nhưng các vấn đề tương tự có thể phát sinh trong các thư viện xử lý các định dạng khác, như TAR, RAR hoặc 7z.
Nguyên nhân cơ bản của vấn đề này là do bên trong các tệp lưu trữ ZIP, mỗi tệp đóng gói được lưu trữ với tên đủ điều kiện, tên này cho phép các ký tự đặc biệt như dấu gạch chéo và dấu chấm. Thư viện mặc định trong gói java.util.zip
không kiểm tra tên của các mục lưu trữ đối với các ký tự truyền tải thư mục (../
). Vì vậy, bạn phải thật cẩn trọng khi liên kết tên được trích xuất từ kho lưu trữ bằng đường dẫn thư mục có mục tiêu.
Bạn cần phải xác thực mọi đoạn mã hoặc thư viện trích xuất ZIP từ các nguồn bên ngoài. Nhiều thư viện như vậy dễ bị quy trình Truyền tải đường dẫn Zip tấn công.
Tác động
Bạn có thể dùng lỗ hổng bảo mật Truyền tải qua đường dẫn Zip để ghi đè tệp tuỳ ý. Tuỳ thuộc vào điều kiện, tác động có thể khác nhau, nhưng trong nhiều trường hợp, lỗ hổng bảo mật này có thể dẫn đến các vấn đề bảo mật lớn như thực thi mã.
Giải pháp giảm thiểu
Để giảm thiểu vấn đề này, trước khi trích xuất từng mục nhập, bạn phải luôn xác minh rằng đường dẫn mục tiêu là con của thư mục đích. Đoạn mã dưới đây giả định rằng thư mục đích là an toàn – chỉ ứng dụng của bạn mới có thể ghi và thư mục không thuộc quyền kiểm soát của kẻ tấn công – nếu không, ứng dụng của bạn có thể dễ bị các lỗ hổng bảo mật khác như các cuộc tấn công liên kết tượng trưng.
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;
}
Để tránh vô tình ghi đè tệp hiện có, bạn cũng phải đảm bảo rằng thư mục đích trống trước khi bắt đầu quá trình trích xuất. Nếu không, ứng dụng của bạn có nguy cơ gặp sự cố hoặc trong các trường hợp nghiêm trọng, ứng dụng sẽ bị xâm phạm.
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);
…
}
}
}
Tài nguyên
Đề xuất cho bạn
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2023-12-14 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2023-12-14 UTC."],[],[],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)"]]