2018/02/20
信頼できない ZIP ファイルは ZipInputStream で開いてはいけない
@Java 1.8.0
`ZipInputStream` から取得した `ZipEntry` の `getSize()` は `-1` になることがあります。
```java
`highlight: 6;
private void read(InputStream is) throws IOException {
ZipInputStream zis = new ZipInputStream(is);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// getSize() が -1 になることがある
Log.d(TAG, entry.getName() + "'s size is " + entry.getSize());
}
}
```
何故なのか調べてみたところ、ZIP ファイルは**末尾の Central Directory にデータ情報がある**ため、Stream で読みだした時はその情報が正しいか保証されてないのです。
Wikipedia に詳しいフォーマットが載っていたので詳細はそちらを参照してください。
> 参考
>
> [ZIP (ファイルフォーマット) - Wikipedia](https://ja.wikipedia.org/wiki/ZIP_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)
### ローカルヘッダは信用できない
`ZipInputStream` の `ZipEntry` が返すのは 各ローカルファイルヘッダの値であり、それらが正しい保証はありません。
また、エントリを残したまま、Central Directory から削除してしまうことも可能です。
そうなると、本来消したはずのファイルが存在するかのように扱われてしまいます。
OS のファイルシステムではよく使われる手ですが、ZIP にはそぐわない気がします。
### ZipFile を使う方が良い
`ZipFile` は最初に Central Directory を見に行くようです。
自分で圧縮した物等、ある程度信頼できるものは別ですが、他者が作った ZIP ファイルは `ZipFile` で開いた方が無難です。
### 悪意のある ZIP ファイルにも気をつける
上記は悪意はなくとも問題が起こるケースですが、悪意のある ZIP ファイルを不用意に展開してしまうと `ZipFile` を使っていても問題が起こる可能性があります。
具体的には次のようなエントリを持つファイルです。
- 正規化すると展開先ディレクトリの外を指すパスになるようなファイル名を持つエントリ
- 展開処理によってシステムリソースを過大に消費するようなエントリ
以下では ZipInputStream についてのみ書いてありますが、`ZipFile` も同じ問題を抱えていると言えるでしょう。
> 参考
>
> [IDS04-J. ZipInputStream からファイルを安全に展開する](https://www.jpcert.or.jp/java-rules/ids04-j.html)

0 件のコメント:
コメントを投稿