当我解压文件时,它的文件名类似于Movies\Hollywood\spider-Man。 实际上Movies是一个文件夹,Hollywood是电影中的文件夹,spider-Man是好莱坞中的文件。
如果 Movies\Hollywood\spider-Man 在创建 zip 时是一个文件,则无论其是否有扩展名(如 *.mp4、*.flv),都应将其提取为文件
您可以依赖命名空间 java.util.zip 下的 java API,文档链接是这里
编写了一些仅提取zip文件的代码,它应该将文件条目提取为文件(不支持gzip,rar)。
private boolean extractFolder(File destination, File zipFile) throws ZipException, IOException
{
int BUFFER = 8192;
File file = zipFile;
//This can throw ZipException if file is not valid zip archive
ZipFile zip = new ZipFile(file);
String newPath = destination.getAbsolutePath() + File.separator + FilenameUtils.removeExtension(zipFile.getName());
//Create destination directory
new File(newPath).mkdir();
Enumeration zipFileEntries = zip.entries();
//Iterate overall zip file entries
while (zipFileEntries.hasMoreElements())
{
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
File destinationParent = destFile.getParentFile();
//If entry is directory create sub directory on file system
destinationParent.mkdirs();
if (!entry.isDirectory())
{
//Copy over data into destination file
BufferedInputStream is = new BufferedInputStream(zip
.getInputStream(entry));
int currentByte;
byte data[] = new byte[BUFFER];
//orthodox way of copying file data using streams
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
}
return true;//some error codes etc.
}
该例程不执行任何异常处理,请捕获驱动程序代码中的 ZipException 和 IOException。
我的应用程序包含 A.B. 修复 Zip 路径遍历漏洞的答案的 Kotlin 版本。
关于修复 Zip 路径遍历漏洞(来自网站 https://support.google.com/faqs/answer/9294009):
“Zip 文件可以包含名称中包含路径遍历字符(“../”)的条目(文件或目录)。如果开发人员在未验证其名称的情况下解压缩此类 zip 文件条目,则可能会导致路径遍历攻击,从而导致写入任意目录,甚至覆盖应用程序私有文件夹中的文件。
我们建议通过检查解压文件的规范路径是否位于预期目录下来修复应用程序中的此问题。具体来说,在使用通过 ZipEntry 的 getName() 方法的返回值创建的 File 对象之前,请务必检查 File.GetCanonicalPath() 的返回值是否属于预期的目录路径。"
/**
* unzip file with subdirectories.
*
* Refer to [stackoverflow](https://stackoverflow.com/questions/43672241/how-to-unzip-file-with-sub-directories-in-android)
* answer of [A.B.](https://stackoverflow.com/users/4914757/a-b)
* Refer to [support.google.com](https://support.google.com/faqs/answer/9294009)
* Refer to [stackoverflow](https://stackoverflow.com/questions/56303842/fixing-a-zip-path-traversal-vulnerability-in-android)
* answer of [Indra Kumar S](https://stackoverflow.com/users/3577946/indra-kumar-s)
*
* @param destination file
* @param zipFile file
* @return boolean
*/
@Throws(ZipException::class, IOException::class)
private fun extractFolder(destination: File, zipFile: File): Boolean {
val BUFFER = 8192
// File file = zipFile;
//This can throw ZipException if file is not valid zip archive
val zip = ZipFile(zipFile)
// String newPath = destination.getAbsolutePath() + File.separator + FilenameUtils.removeExtension(zipFile.getName());
val newPath = destination.absolutePath + File.separator + stripExtension(
zipFile.name.substring(zipFile.name.lastIndexOf("/") + 1)
)
//Create destination directory
File(newPath).mkdir()
val zipFileEntries: Enumeration<*> = zip.entries()
//Iterate overall zip file entries
while (zipFileEntries.hasMoreElements()) {
val entry = zipFileEntries.nextElement() as ZipEntry
val currentEntry = entry.name
val destFile = File(destination.absolutePath, currentEntry)
//
// String canonicalPath = destFile.getCanonicalPath();
try {
ensureZipPathSafety(destFile, destination)
} catch (e: Exception) {
// SecurityException
e.printStackTrace()
return false
}
// if (!canonicalPath.startsWith(destination.getAbsolutePath())) {
// SecurityException
// }
// Finish unzipping…
//
val destinationParent = destFile.parentFile
//If entry is directory create sub directory on file system
destinationParent!!.mkdirs()
if (!entry.isDirectory) {
//Copy over data into destination file
val `is` = BufferedInputStream(
zip
.getInputStream(entry)
)
var currentByte: Int
val data = ByteArray(BUFFER)
//orthodox way of copying file data using streams
val fos = FileOutputStream(destFile)
val dest = BufferedOutputStream(fos, BUFFER)
while (`is`.read(data, 0, BUFFER).also { currentByte = it } != -1) {
dest.write(data, 0, currentByte)
}
dest.flush()
dest.close()
`is`.close()
}
}
return true //some error codes etc.
}
/**
* ensure Zip Path Safety.
*
*
* Refer to [stackoverflow](https://stackoverflow.com/questions/56303842/fixing-a-zip-path-traversal-vulnerability-in-android)
* answer of [Indra Kumar S](https://stackoverflow.com/users/3577946/indra-kumar-s)
*
* @param destFile file
* @param destination directory
*/
@Throws(Exception::class)
private fun ensureZipPathSafety(destFile: File, destination: File) {
val destDirCanonicalPath = destination.canonicalPath
val destFileCanonicalPath = destFile.canonicalPath
if (!destFileCanonicalPath.startsWith(destDirCanonicalPath)) {
throw Exception(
String.format(
"Found Zip Path Traversal Vulnerability with %s",
destFileCanonicalPath
)
)
}
}
/**
* strip file name extension.
*
*
* Refer to [stackoverflow](https://stackoverflow.com/questions/7541550/remove-the-extension-of-a-file)
* answer of [palacsint](https://stackoverflow.com/users/843804/palacsint)
*
* @param s string file name
* @return string file name without extension
*/
fun stripExtension(s: String?): String {
return if (s != null && s.lastIndexOf(".") > 0) s.substring(
0,
s.lastIndexOf(".")
) else s!!
}