ZipSecurity.java 2.3 KB
Newer Older
S
Sergey Toshin 已提交
1 2 3
package jadx.core.utils.files;

import java.io.File;
4
import java.io.IOException;
S
Sergey Toshin 已提交
5 6
import java.util.zip.ZipEntry;

7 8 9
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

S
Sergey Toshin 已提交
10
public class ZipSecurity {
11
	private static final Logger LOG = LoggerFactory.getLogger(ZipSecurity.class);
S
Skylot 已提交
12

S
Sergey Toshin 已提交
13
	// size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times
S
sergey-wowwow 已提交
14
	private static final int MAX_SIZE_DIFF = 100;
S
Sergey Toshin 已提交
15

S
Skylot 已提交
16 17
	private ZipSecurity() {}

18 19
	private static boolean isInSubDirectoryInternal(File baseDir, File canonFile) {
		if (canonFile == null) {
S
Skylot 已提交
20 21
			return false;
		}
22
		if (canonFile.equals(baseDir)) {
S
Skylot 已提交
23 24
			return true;
		}
25 26 27 28 29 30
		return isInSubDirectoryInternal(baseDir, canonFile.getParentFile());
	}
	
	public static boolean isInSubDirectory(File baseDir, File file) {
		try {
			file = file.getCanonicalFile();
31
			baseDir = baseDir.getCanonicalFile();
32 33 34 35 36
		}
		catch(IOException e) {
			return false;
		}
		return isInSubDirectoryInternal(baseDir, file);
S
Sergey Toshin 已提交
37
	}
S
Skylot 已提交
38

S
Sergey Toshin 已提交
39 40 41 42 43 44
	// checks that entry name contains no any traversals
	// and prevents cases like "../classes.dex", to limit output only to the specified directory
	public static boolean isValidZipEntryName(String entryName) {
		try {
			File currentPath = new File(".").getCanonicalFile();
			File canonical = new File(currentPath, entryName).getCanonicalFile();
45
			if (isInSubDirectoryInternal(currentPath, canonical)) {
46 47
				return true;
			}
48
			LOG.error("Path traversal attack detected, invalid name: {}", entryName);
49
			return false;
S
Skylot 已提交
50
		} catch (Exception e) {
51
			LOG.error("Path traversal attack detected, invalid name: {}", entryName);
S
Sergey Toshin 已提交
52 53 54
			return false;
		}
	}
S
Skylot 已提交
55

S
Sergey Toshin 已提交
56
	public static boolean isZipBomb(ZipEntry entry) {
S
Skylot 已提交
57
		long compressedSize = entry.getCompressedSize();
S
Sergey Toshin 已提交
58
		long uncompressedSize = entry.getSize();
S
Skylot 已提交
59
		if (compressedSize < 0 || uncompressedSize < 0) {
60 61
			LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
					compressedSize, uncompressedSize, entry.getName());
S
Sergey Toshin 已提交
62 63
			return true;
		}
S
Skylot 已提交
64 65
		if (compressedSize * MAX_SIZE_DIFF < uncompressedSize) {
			LOG.error("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
66
					compressedSize, uncompressedSize, entry.getName());
67 68 69
			return true;
		}
		return false;
S
Sergey Toshin 已提交
70
	}
S
Skylot 已提交
71

S
Sergey Toshin 已提交
72 73 74 75 76
	public static boolean isValidZipEntry(ZipEntry entry) {
		return isValidZipEntryName(entry.getName())
				&& !isZipBomb(entry);
	}
}