mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
修复ZIP bomb漏洞
This commit is contained in:
parent
85c1c84af1
commit
31c5357409
@ -40,8 +40,8 @@
|
|||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=jPq9DsjXs7GUWbXRZU3wygSJyMEy4pqr&jump_from=webapi">
|
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=QtsqXLkHpLjE99tkre19j6pjPMhSay1a&jump_from=webapi">
|
||||||
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A5-610134978-orange"/></a>
|
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A5-715292493-orange"/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
@ -40,8 +40,8 @@
|
|||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=jPq9DsjXs7GUWbXRZU3wygSJyMEy4pqr&jump_from=webapi">
|
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=QtsqXLkHpLjE99tkre19j6pjPMhSay1a&jump_from=webapi">
|
||||||
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A5-610134978-orange"/></a>
|
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A5-715292493-orange"/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.hutool.core.compress;
|
package cn.hutool.core.compress;
|
||||||
|
|
||||||
|
import cn.hutool.core.exceptions.ValidateException;
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
@ -25,6 +26,9 @@ import java.util.zip.ZipInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ZipReader implements Closeable {
|
public class ZipReader implements Closeable {
|
||||||
|
|
||||||
|
// size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times
|
||||||
|
private static final int MAX_SIZE_DIFF = 100;
|
||||||
|
|
||||||
private ZipFile zipFile;
|
private ZipFile zipFile;
|
||||||
private ZipInputStream in;
|
private ZipInputStream in;
|
||||||
|
|
||||||
@ -202,7 +206,7 @@ public class ZipReader implements Closeable {
|
|||||||
private void readFromZipFile(final Consumer<ZipEntry> consumer) {
|
private void readFromZipFile(final Consumer<ZipEntry> consumer) {
|
||||||
final Enumeration<? extends ZipEntry> em = zipFile.entries();
|
final Enumeration<? extends ZipEntry> em = zipFile.entries();
|
||||||
while (em.hasMoreElements()) {
|
while (em.hasMoreElements()) {
|
||||||
consumer.accept(em.nextElement());
|
consumer.accept(checkZipBomb(em.nextElement()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,11 +219,32 @@ public class ZipReader implements Closeable {
|
|||||||
private void readFromStream(final Consumer<ZipEntry> consumer) throws IORuntimeException {
|
private void readFromStream(final Consumer<ZipEntry> consumer) throws IORuntimeException {
|
||||||
try {
|
try {
|
||||||
ZipEntry zipEntry;
|
ZipEntry zipEntry;
|
||||||
while (null != (zipEntry = in.getNextEntry())) {
|
while (null != (zipEntry = checkZipBomb(in.getNextEntry()))) {
|
||||||
consumer.accept(zipEntry);
|
consumer.accept(zipEntry);
|
||||||
}
|
}
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查Zip bomb漏洞
|
||||||
|
*
|
||||||
|
* @param entry {@link ZipEntry}
|
||||||
|
* @return 检查后的{@link ZipEntry}
|
||||||
|
*/
|
||||||
|
private static ZipEntry checkZipBomb(final 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 ValidateException("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
|
||||||
|
compressedSize, uncompressedSize, entry.getName());
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@ package cn.hutool.core.compress;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.iter.EnumerationIter;
|
import cn.hutool.core.collection.iter.EnumerationIter;
|
||||||
import cn.hutool.core.exceptions.UtilException;
|
import cn.hutool.core.exceptions.UtilException;
|
||||||
import cn.hutool.core.io.stream.FastByteArrayOutputStream;
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.io.file.FileSystemUtil;
|
import cn.hutool.core.io.file.FileSystemUtil;
|
||||||
import cn.hutool.core.io.file.PathUtil;
|
import cn.hutool.core.io.file.PathUtil;
|
||||||
import cn.hutool.core.io.resource.Resource;
|
import cn.hutool.core.io.resource.Resource;
|
||||||
|
import cn.hutool.core.io.stream.FastByteArrayOutputStream;
|
||||||
|
import cn.hutool.core.io.stream.LimitedInputStream;
|
||||||
import cn.hutool.core.text.StrUtil;
|
import cn.hutool.core.text.StrUtil;
|
||||||
import cn.hutool.core.util.CharUtil;
|
import cn.hutool.core.util.CharUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
@ -77,7 +78,7 @@ public class ZipUtil {
|
|||||||
*/
|
*/
|
||||||
public static InputStream getStream(final ZipFile zipFile, final ZipEntry zipEntry) {
|
public static InputStream getStream(final ZipFile zipFile, final ZipEntry zipEntry) {
|
||||||
try {
|
try {
|
||||||
return zipFile.getInputStream(zipEntry);
|
return new LimitedInputStream(zipFile.getInputStream(zipEntry), zipEntry.getSize());
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
65
hutool-core/src/main/java/cn/hutool/core/io/stream/LimitedInputStream.java
Executable file
65
hutool-core/src/main/java/cn/hutool/core/io/stream/LimitedInputStream.java
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
package cn.hutool.core.io.stream;
|
||||||
|
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制读取最大长度的{@link FilterInputStream} 实现<br>
|
||||||
|
* 来自:https://github.com/skylot/jadx/blob/master/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/LimitedInputStream.java
|
||||||
|
*
|
||||||
|
* @author jadx
|
||||||
|
*/
|
||||||
|
public class LimitedInputStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private final long maxSize;
|
||||||
|
private long currentPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param in {@link InputStream}
|
||||||
|
* @param maxSize 限制最大读取量,单位byte
|
||||||
|
*/
|
||||||
|
public LimitedInputStream(final InputStream in, final long maxSize) {
|
||||||
|
super(in);
|
||||||
|
this.maxSize = maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
final int data = super.read();
|
||||||
|
if (data != -1) {
|
||||||
|
currentPos++;
|
||||||
|
checkPos();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
|
@Override
|
||||||
|
public int read(final byte[] b, final int off, final int len) throws IOException {
|
||||||
|
final int count = super.read(b, off, len);
|
||||||
|
if (count > 0) {
|
||||||
|
currentPos += count;
|
||||||
|
checkPos();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(final long n) throws IOException {
|
||||||
|
final long skipped = super.skip(n);
|
||||||
|
if (skipped != 0) {
|
||||||
|
currentPos += skipped;
|
||||||
|
checkPos();
|
||||||
|
}
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPos() {
|
||||||
|
if (currentPos > maxSize) {
|
||||||
|
throw new IllegalStateException("Read limit exceeded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user