对java.util.ZipInputStream 的输入进行检查可以防止消耗过多的系统资源。解压一个文件,比如 zip、gif或者 gzip编码的 HTTP 内容,可能会消耗过多的资源,并且在压缩率极高的情况下,可能会导 致 zip炸弹的出现。
对于从 ZipInputStream 安全解压文件的情况,示例1给出了不规范用法(Java 语言)示例。示例2 给出了规范用法(Java 语言)示例。
java
示例1:
static final int BUFFER = 512;
/1.
public final void unzip(String filename) throws java.io.IOException{
FileInputStreamfis = new FileInputStream(filename);
ZipInputStreamzis = new ZiplnputStream(new BufferedInputStream(fis));
ZipEntry entry;
try {
while((entry = zis.getNextEntry())!= null){
System.out.println("Extracting:"+ entry);
int count;
byte data[] = new byte[BUFFER];
// Write the files to the disk
FileOutputStreamfos = new FileOutputStream(entry.getName());
BufferedOutputStreamdest = new BufferedOutputStream(fos,BUFFER);
while((count = zis.read(data,0,BUFFER))!=- 1){
dest.write(data,0,count);
dest.flush();
dest.close();
zis.closeEntry();
} finally{
zis.close();
}
这个不规范的代码示例没有检查解压一个文件时所消耗的资源。它会允许操作持续进行直至完 成,或者直至本地资源耗尽为止。
java
示例2:
static final int BUFFER = 512;
static final int TOOBIG = 0x6400000;// Max size of unzipped data,100MB
static final int TOOMANY = 1024; // Max number of files
//..
private String validateFilename(String filename, String intendedDir)
throws java.io.IOException {
File f = new File(filename);
String canonicalPath = f.getCanonicalPath();
File iD = new File(intendedDir);
String canonicalID= iD.getCanonicalPath();
if (canonicalPath.startsWith(canonicalID)){
return canonicalPath;
}else {
throw new lllegalStateException("File is outside extraction target directory.");
public final void unzip(String filename) throws java.io.IOException {
FileInputStreamfis = new FileInputStream(filename);
ZipInputStreamzis = new ZiplnputStream(new BufferedInputStream(fis));
ZipEntry entry;
int entries = 0;
long total = 0;
try {
while((entry = zis.getNextEntry())!= null){
System.out.println("Extracting:"+ entry);
int count;
byte data[]= new byte[BUFFER];
// Write the files to the disk, but ensure that the filename is valid,
// and that the file is not insanely big
String name = validateFilename(entry.getName(),".");
if (entry.isDirectory()){
System.out.println("Creating directory"+ name);
new File(name),mkdir();
continue;
FileOutputStreamfos = new FileOutputStream(name);
BufferedOutputStreamdest = new BufferedOutputStream(fos, BUFFER);
while(total+ BUFFER <= TOOBIG &.&.(count = zis.read(data,0,BUFFER))!=- 1){
dest.write(data,0,count);
total += count;
dest.flush();
dest.close();
zis.closeEntry();
entries++;
if (entries 〉TOOMANY){
throw new lllegalStateException("Too many files to unzip.");
if (total 〉TOOBIG){
throw new IllegalStateException("File being unzipped is too big.");
} finally {
zis.close();
}
在这个规范的代码示例中,代码在提取条目之前验证每个条目的名称。如果名称无效,那么整个提 取就会被中止。 while 循环中的代码将判断 zip归档中每个条目的文件大小,同时提取条目。如果提取 的条目太大,在本例中为100 MB, 则会抛出异常。代码不要使用ZipEntry.getSize()方法,因为攻击者 可以伪造 ZIP 文档中未压缩的文件的大小。最后,代码还计算压缩包中文件条目的数量,如果超过 1024个条目,则抛出异常。