码迷,mamicode.com
首页 > 其他好文 > 详细

zip解压及zip炸弹的防御

时间:2020-01-22 01:16:37      阅读:346      评论:0      收藏:0      [点我收藏+]

标签:else   let   大小   tput   info   验证   return   sdi   长度   

解压功能验证正常,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炸弹的防御

标签:else   let   大小   tput   info   验证   return   sdi   长度   

原文地址:https://www.cnblogs.com/hello4world/p/12227391.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!