You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache Commons Compress防Zip Bomb未生效问题排查

问题描述

现有基于java.util.zip库实现的Zip文件解压Java代码,在SonarQube扫描时被检出安全热点漏洞,提示存在Zip Bomb(压缩包炸弹)安全风险,告警位置在代码行ZipEntry entry = zipIn.getNextEntry();,告警信息为:

“Make sure that expanding this archive file is safe here(请确认在此处解压归档文件的操作具备安全性)”

为修复该问题,开发人员尝试替换为Apache Commons Compress 1.21版本实现解压逻辑——该组件自1.17版本起已内置Zip Bomb防护能力。为验证防护效果,开发人员使用公开的Zip Bomb测试样本开展验证测试,但测试过程中恶意压缩包被正常解压,全程未抛出任何错误或异常,需要排查该Commons Compress解压代码存在的问题。

Maven依赖配置
<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-compress</artifactId>
 <version>1.21</version>
</dependency>
存在Zip Bomb风险的原始JDK实现代码
private void unzipNormal(String zipFilePath, String destDirectory) {
    try {
        File destDir = new File(destDirectory);
        if(!destDir.exists()) {
            destDir.mkdir();
        }

        try(ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath))) {
            ZipEntry entry = zipIn.getNextEntry();
            while(entry != null) {
                String filePath = destDirectory + File.separator + entry.getName();
                if(!entry.isDirectory()) {
                    extractFile(zipIn, filePath);
                } else {
                    File dir = new File(filePath);
                    dir.mkdir();
                }
                zipIn.closeEntry();
                entry = zipIn.getNextEntry();
            }
            zipIn.close();
        }

    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

private static void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
    try(BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
        byte[] bytesIn = new byte[4096];
        int read = 0;
        while((read = zipIn.read(bytesIn)) != -1) {
            bos.write(bytesIn, 0, read);
        }
        bos.close();
    } catch (Exception ex) {
        ex.printStackTrace();
        throw ex;
    }
}
基于Apache Commons Compress实现的问题代码
private void unzip(String srcZipFile, String destFolder) throws IOException {

        Path filePath = Paths.get(srcZipFile);

        try(InputStream inputStream = Files.newInputStream(filePath);
            ZipArchiveInputStream i = new ZipArchiveInputStream(inputStream)
        ) {
            System.out.println("Begin..");
            ArchiveEntry entry = null;
            while((entry = i.getNextEntry()) != null) {
                if(!i.canReadEntryData(entry)) {
                    System.out.println("Continue..");
                    continue;
                }

                Path path = Paths.get(destFolder, entry.getName());
                File f = path.toFile();
                if(entry.isDirectory()) {
                    if (!f.isDirectory() && !f.mkdirs()) {
                        throw new IOException("failed to create directory " + f);
                    }
                } else {
                    File parent = f.getParentFile();
                    if(!parent.isDirectory() && !parent.mkdirs()) {
                        throw new IOException("failed to create directory " + parent);
                    }
                    try (OutputStream o = Files.newOutputStream(f.toPath())) {
                        IOUtils.copy(i, o);
                    }
                }

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
}
问题根因

这段代码没有拦截到Zip Bomb的核心原因非常直接:Apache Commons Compress的Zip Bomb防护能力默认是关闭的。
代码中使用new ZipArchiveInputStream(inputStream)这个最简构造方法实例化解压流时,没有传入任何安全阈值配置,组件内置的压缩比异常检测、单文件解压大小上限、总解压大小上限这些防护逻辑全程不会触发,恶意压缩包自然会被正常解压。
Commons Compress的防护规则需要开发者结合自身业务场景手动配置才能生效,三个核心配置阈值如下:

  • 最大压缩比:解压后文件大小/压缩包内对应条目原始大小的比值,超过该值则判定为高压缩比恶意文件,直接抛出异常终止解压
  • 单条目解压上限:单个压缩条目解压后的大小超过设定值时直接拦截
  • 总解压大小上限:整个压缩包所有内容解压后的累计大小超过阈值时终止解压
修复方案

构造ZipArchiveInputStream时传入自定义的阈值配置即可开启防护,示例代码如下:

// 根据业务场景配置安全阈值:此处示例为最大压缩比100,单文件最大1GB,总解压大小最大10GB
ThresholdIterator thresholds = Thresholds.builder()
        .withMaxCompressionRatio(100)
        .withMaxEntrySize(1024L * 1024 * 1024)
        .withMaxTotalSize(10L * 1024 * 1024 * 1024)
        .get();
try(InputStream inputStream = Files.newInputStream(filePath);
    ZipArchiveInputStream zipIn = new ZipArchiveInputStream(inputStream, "UTF-8", true, false, thresholds)
) {
    // 原有解压逻辑保持不变
}

配置完成后,当解压的压缩包触发任意阈值规则时,代码会抛出ZipException终止解压流程,实现Zip Bomb防护。


内容的提问来源于stack exchange,提问作者Vasanth Subramanian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:12:34