zip解压及zip炸弹的防御

Posted hello4world

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了zip解压及zip炸弹的防御相关的知识,希望对你有一定的参考价值。

解压功能验证正常,zip炸弹防御部分还没验证完,后续验证后再确认

    private static final int MAX_COUNT = 10000;
    // 注意,long类型后面要加L
    private static final long MAX_SIZE = 4L * 1024 * 1024 * 1024;
    private static final int PATH_LENGTH = 512;

    /**
     *  zip解压及zip炸弹的防御
     *   防御要点:1.校验解压后文件大小 2.校验解压后的条目总数 3.解压时防止跨目录攻击
     *   4.校验解压文件路径 5.校验文件路径长度
     * @param zipFilePath
     */
    public static void unzip(String zipFilePath) throws Exception {
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        // 判断是否zip文件
        if (!isZipFile(zipFilePath)) {
            return;
        }
        // 创建解压文件存放目录
        File file = new File("D:\unzip");
        if (!file.exists()) {
            file.mkdirs();
        }
        // 使用ZipFile获得解压文件对象,第二个参数是编码
        ZipFile zipFile = new ZipFile(zipFilePath, Charset.forName("GBK"));
        // 用Enumeration接收压缩文件中的每一个条目(目录或文件)
        Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
        // 统计条目数量
        int count = 0;
        // 统计解压文件大小
        long size = 0L;
        // 遍历zipEntries
        while (zipEntries.hasMoreElements()) {
            // 校验zip解压条目总数
            if (++count > MAX_COUNT) {
                //TODO delete unzip files
                return;
            }
            // 获得压缩文件中此次的条目
            ZipEntry zipEntry = zipEntries.nextElement();
            size += zipEntry.getSize();
            // 校验解压后的文件大小
            if (size > MAX_SIZE) {
                // TODO delete unzip files
                return;
            }
            // 获取此条目(文件或目录)的路径
            String path = "D:\unzip\" + zipEntry.getName();
            // 校验文件路径长度
            if (path.length() > PATH_LENGTH) {
                // TODO delete unzip file
                return;
            }
            // 判断解压文件路径是否合法
            if (!isLegalPath(path)) {
                // TODO delete unzip files
                return;
            }
            // 是目录则创建文件夹,是文件则用流读写出来
            LOG.info("判断是文件夹还是文件");
            if (zipEntry.isDirectory()) {
                LOG.info("是文件夹");
                new File(path).mkdirs();
            } else {
                try {
                    LOG.info("开始用流读写文件");
                    inputStream = zipFile.getInputStream(zipEntry);
                    fileOutputStream = new FileOutputStream(path);
                    byte[] data = new byte[8 * 1024];
                    int num = -1;
                    while ((num = inputStream.read(data)) != -1) {
                        fileOutputStream.write(data, 0, num);
                    }
                } finally {
                    if (fileOutputStream != null) {
                        fileOutputStream.close();
                    }
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }
            }
        }
    }

    /**
     * 判断是否是zip文件
     * @param zipFilePath
     * @return
     */
    private static boolean isZipFile(String zipFilePath) {
        if (zipFilePath == null) {
            return false;
        }
        File file = new File(zipFilePath);
        if (file.exists() && file.getName().endsWith("zip")) {
            return true;
        }
        return false;
    }

    /**
     *  判断解压文件路径是否合法
     * @param path
     * @return
     */
    private static boolean isLegalPath(String path) throws Exception {
        if (path == null) {
            return false;
        }
        if (new File(path).getCanonicalPath().startsWith("D:\unzip\")) {
            return true;
        }
        return false;
    }

修正后删除本句话

以上是关于zip解压及zip炸弹的防御的主要内容,如果未能解决你的问题,请参考以下文章

使用JAVA解压加密的中文ZIP压缩包

JAVA解压.TAR.Z及.ZIP文件

ZIP压缩算法详细分析及解压实例解释

Python调用7zip命令实现解压

用win rar压缩的文件能用7-ZIP解压吗

用win rar压缩的文件能用7-ZIP解压吗