有研发的同学询问zip slip漏洞的修复,碰巧最近在批量找一些cms的zip slip漏洞,提供以下两种修复方式,仅供参考:
修复方式1
private static final int DEFAULT_BUFFER_SIZE = 8192;
public static void unzip(File zipFile, File destDir, String encoding) {
if (destDir.exists() && !destDir.isDirectory()) {
throw new IllegalArgumentException("destDir is not a directory!");
}
ZipFile zip = null;
InputStream is = null;
FileOutputStream fos = null;
File file;
String name;
byte[] buff = new byte[DEFAULT_BUFFER_SIZE];
int read;
ZipEntry entry;
try {
try {
if (StringUtils.isNotBlank(encoding)) {
zip = new ZipFile(zipFile, encoding);
} else {
zip = new ZipFile(zipFile);
}
Enumeration<?> en = zip.getEntries();
while (en.hasMoreElements()) {
entry = (ZipEntry) en.nextElement();
name = entry.getName();
name = name.replace('/', File.separatorChar);
// 防止zip包里面的使用`../..`之类的文件名,将文件解压到指定目录之外。
name = name.replace("..", "");
file = new File(destDir, name);
if (entry.isDirectory()) {
file.mkdirs();
} else {
// 创建父目录
file.getParentFile().mkdirs();
is = zip.getInputStream(entry);
fos = new FileOutputStream(file);
while ((read = is.read(buff)) > 0) {
fos.write(buff, 0, read);
}
fos.close();
is.close();
}
}
} finally {
if (fos != null) {
fos.close();
}
if (is != null) {
is.close();
}
if (zip != null) {
zip.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
zip解压主要使用的是ant组件,依赖相关如下:
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
修复方式2
public static void unZipFiles(File zipFile,String descDir) throws IOException {
File pathFile = new File(descDir);
if (!pathFile.exists()) {
pathFile.mkdirs();
}
// 解决zip文件中有中文目录或者中文文件
ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));
for (Enumeration entries = zip.entries(); entries.hasMoreElements();) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryName = entry.getName();
Pattern pattern = Pattern.compile("[^a-zA-Z0-9_\\-\\.]");
Matcher matcher = pattern.matcher(entryName);
if(entryName.contains("../") || entryName.contains("..\\") || matcher.find()){
System.out.println("压缩包中文件名疑似不安全,详情: "+ entryName);
throw new IOException("压缩包中文件名疑似不安全,详情: "+ entryName);
}
InputStream in = zip.getInputStream(entry);
String outPath = Paths.get(descDir, entryName).toString().replaceAll("\\*", "/");
// 判断路径是否存在,不存在则创建文件路径
File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
if (!file.exists()) {
file.mkdirs();
}
// 判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
if (new File(outPath).isDirectory()) {
continue;
}
OutputStream out = new FileOutputStream(outPath);
byte[] buf1 = new byte[1024];
int len;
while ((len = in.read(buf1)) > 0) {
out.write(buf1, 0, len);
}
in.close();
out.close();
}
zip.close();
}
修复方式2,代码直接使用java.util.zip,未使用第三方组件
另外Hutool也提供解压方法,Hutool在4.1.12之前的版本存在zip slip漏洞,这点需要注意。