自学内容网 自学内容网

hutool 解压缩读取源文件和压缩文件大小失败导致报错

前言

最近处理老项目中的问题,升级安全jar,发现hutool的jar在解压缩的时候报错了,实际上是很简单的防御zip炸弹攻击的手段,但是却因为hutool的工具包取文件大小有bug,造成了解压缩不能用,报错:invalid sizes: compressed -1, uncompressed -1,理论上使用这个API的所有方法都有问题。影响范围hutool 5.8.11~5.8.16,5.8.17修复。

Exception in thread "main" cn.hutool.core.exceptions.UtilException: Zip bomb attack detected, invalid sizes: compressed -1, uncompressed -1, name /Users/huahua/Downloads/zip-demo/1.exe
at cn.hutool.core.compress.ZipReader.checkZipBomb(ZipReader.java:247)
at cn.hutool.core.compress.ZipReader.readFromStream(ZipReader.java:224)
at cn.hutool.core.compress.ZipReader.read(ZipReader.java:188)
at cn.hutool.core.compress.ZipReader.readTo(ZipReader.java:148)
at cn.hutool.core.compress.ZipReader.readTo(ZipReader.java:135)
at cn.hutool.core.util.ZipUtil.unzip(ZipUtil.java:665)
at cn.hutool.core.util.ZipUtil.unzip(ZipUtil.java:650)

demo准备

构建一个demo吧:JDK8+hutool 5.8.16

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("/Users/huahua/Downloads/zip-demo/1.exe");
        File zipFile = new File("/Users/huahua/Downloads/zip-demo/zip-demo.zip");
        ZipUtil.zip(zipFile, "/Users/huahua/Downloads/zip-demo/1.exe", new FileInputStream(file));

        ZipUtil.unzip(new FileInputStream(zipFile), new File("/Users/huahua/Downloads/zip-demo/2.exe"), null);

        System.out.println("Hello world!");
    }
}

没考虑流关闭问题,实际生产中使用try with resource即可

运行报错invalid sizes: compressed -1, uncompressed -1,这里的-1是文件大小,明显是取值不对

但是使用文件方式,确可以成功

从而确定是通过流的方式取文件大小是有问题的。 

原因

hutool实际上在5.8.10之前是没有检验zip炸弹的,从安全漏洞网站,可以看到出现:Hutool资源消耗漏洞 CVE-2022-4565Hutool资源消耗漏洞 CVE-2022-4565 - FreeBuf网络安全行业门户

为了解决这个漏洞,实际上就说zip炸弹攻击会消耗很多CPU资源,因为解压缩后要写文件,要存储,很可能造成DDOS和磁盘爆满。

引入了检查,默认是100倍的压缩比率,超过了也会报错,认为是zip炸弹,这个有点武断了,所以有个参数控制跳过,但是没有参数设置比率。直接从源码cn.hutool.core.compress.ZipReader分析

5.8.10版本,并没有检查zip压缩的比率,直接读取zip文件对象去解压了

/**
 * 读取并处理Zip流中的每一个{@link ZipEntry}
 *
 * @param consumer {@link ZipEntry}处理器
 * @throws IORuntimeException IO异常
 */
private void readFromStream(Consumer<ZipEntry> consumer) throws IORuntimeException {
try {
ZipEntry zipEntry;
while (null != (zipEntry = in.getNextEntry())) {
consumer.accept(zipEntry);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
}

升级5.8.11,按照100倍检查,超过100倍认为是zip炸弹,但是万一确实100倍怎么办,在5.8.21版本之前是没办法的,5.8.21做了跳过处理

// size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times
private static final int MAX_SIZE_DIFF = 100;
/**
 * 检查Zip bomb漏洞
 *
 * @param entry {@link ZipEntry}
 * @return 检查后的{@link ZipEntry}
 */
private static ZipEntry checkZipBomb(ZipEntry entry) {
if (null == entry) {
return null;
}
final long compressedSize = entry.getCompressedSize();
final long uncompressedSize = entry.getSize();
if (compressedSize < 0 || uncompressedSize < 0 ||
// 默认压缩比例是100倍,一旦发现压缩率超过这个阈值,被认为是Zip bomb
compressedSize * MAX_SIZE_DIFF < uncompressedSize) {
throw new UtilException("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
compressedSize, uncompressedSize, entry.getName());
}
return entry;
}

 那么为什么5.8.16取文件流的文件大小都是-1呢,在5.8.17修复

因为文件构建的zipentry是有大小属性设置的,而从文件流读取的却丢失了大小属性

 

解决办法也很简单,从文件读取完成信息再检查,反正zip文件还没解压缩,这样zipentry就有文件大小属性了 

超过100倍大小

那么如果zip文件压缩比率超过100倍怎么办,只能升级hutool包,升级5.8.21试试,可以自定义大小倍数,且可以设置<0关闭检查

检查逻辑,以前的常亮改成了变量,且可自定义

但是,缺点依然明显,因为这个设置方法是对象方法,并没有开放配置的API,需要我们自己new 

ZipReader

来自定义设置,原来的API就不能使用了

 

总结

其实hutool工具包很方便,但是在笔者实际项目中经常会出现安全漏洞升级,笔者在分析完源码后在github也找到了相同的问题项:https://github.com/dromara/hutool/issues/3018

实际上很多人都遇到了,相似的jar还有guava经常出现API不兼容啥的,还有安全漏洞升级,不过还是感谢作者提供的开源便利。


原文地址:https://blog.csdn.net/fenglllle/article/details/142415599

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!