2018/02/20

信頼できない ZIP ファイルは ZipInputStream で開いてはいけない

@Java 1.8.0

ZipInputStream から取得した ZipEntrygetSize()-1 になることがあります。

1
2
3
4
5
6
7
8
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 で読みだした時はその情報が正しいか保証されてないのです1


Wikipedia より [By T2y-1979 - CC BY-SA 3.0](https://commons.wikimedia.org/w/index.php?curid=12166527)

Wikipedia に詳しいフォーマットが載っていたので詳細はそちらを参照してください。

参考

ZIP (ファイルフォーマット) - Wikipedia

ローカルヘッダは信用できない

ZipInputStreamZipEntry が返すのは 各ローカルファイルヘッダの値であり、それらが正しい保証はありません。

また、エントリを残したまま、Central Directory から削除してしまうことも可能です。 そうなると、本来消したはずのファイルが存在するかのように扱われてしまいます。 OS のファイルシステムではよく使われる手ですが、ZIP にはそぐわない気がします2

ZipFile を使う方が良い

ZipFile は最初に Central Directory を見に行くようです。

自分で圧縮した物等、ある程度信頼できるものは別ですが、他者が作った ZIP ファイルは ZipFile で開いた方が無難です。

悪意のある ZIP ファイルにも気をつける

上記は悪意はなくとも問題が起こるケースですが、悪意のある ZIP ファイルを不用意に展開してしまうと ZipFile を使っていても問題が起こる可能性があります。

具体的には次のようなエントリを持つファイルです。

  • 正規化すると展開先ディレクトリの外を指すパスになるようなファイル名を持つエントリ
  • 展開処理によってシステムリソースを過大に消費するようなエントリ

以下では ZipInputStream についてのみ書いてありますが、ZipFile も同じ問題を抱えていると言えるでしょう。

参考

IDS04-J. ZipInputStream からファイルを安全に展開する

  1. こんなによく使っているファイル形式なのにちゃんとフォーマットを理解していなかったとは… 
  2. ファイルサイズより実行速度を優先したい場合に行うのでしょう。サイズを小さくしたい圧縮ファイルに、そのような需要があるのかわかりませんが 
?

0 件のコメント: