📑

C#でzipを展開するときに気をつけること

2022/04/24に公開

C#でzipを展開するときに気をつけること

C#では、.NET Framework 4.5から、外部ライブラリを使用することなく、zipファイルを展開可能になりました。
その展開機能を使うときに、ファイル単位で展開する場合には、気をつけるべきポイントがあります。

まとめ

  • ExtractToFile関数とPath.Combine関数を組み合わせると危険
  • ExtractToDirectory関数を利用する(むやみにExtractToFile関数を呼ばない)
  • どうしても使う場合には、フルパスの一致を確認する

Zip Slipとは

細工されたzipファイルのエントリから、ファイルパスの結合を利用して「ディレクトリトラバーサル」(パストラバーサル)を行い、「指定されたフォルダ」の外のファイルを書き換える攻撃です。
zip形式以外にも、いくつかの圧縮形式で、同様の現象が起こせるようです(参考サイトを参照)。

手法

 - folder/a.txt
 - folder/b.jpg
 - c.html
 - ../malicious_file.txt

上のようなファイルを格納するzipファイル(攻撃ファイル)が与えられたとします。
これを以下のコードで展開すると、「指定されたフォルダ」の外にファイルを生成してしまいます。

string currentDirectory = Directory.GetCurrentDirectory();
using (var zip = ZipFile.OpenRead("malicious.zip"))
{
    foreach (var entry in zip.Entries)
    {
        string destPath = Path.Combine(currentDirectory, entry.FullName);
        entry.ExtractToFile(destPath, true);    // ファイルを上書きする
    }
}

カレントディレクトリがC:/testの場合、以下のファイルが作成されます。

 - C:/test/folder/a.txt
 - C:/test/folder/b.jpg
 - C:/test/c.html
 - C:/malicious_file.txt

malicious_file.txtは、「指定されたフォルダ」の外に作成されてしまいました。

参考サイト[1]では、サーバ側の問題について触れられていますが、クライアント側でも発生する可能性があります。

対策

string currentDirectory = Directory.GetCurrentDirectory();
using (var zip = ZipFile.OpenRead("malicious.zip"))
{
    foreach (var entry in zip.Entries)
    {
        // 以下の行を修正
        string destPath = Path.GetFullPath(Path.Combine(currentDirectory, entry.FullName));
        // 以下の4行を追加
        if (!destPath.StartsWith(currentDirectory))
        {
            throw new Exception("Malicious entry has detected.");
        }
        entry.ExtractToFile(destPath, true);    // ファイルを上書きする
    }
}

Path.GetFullPath()関数で、結合したファイルパスを検証し、「指定されたフォルダ」の外であれば、エラーとします。[2]
今回は例外を投げるようにしました。

また一般的には、zip内のすべてのファイルを展開することが多いと思いますので、ライブラリで対策されているZipFile.ExtractToDirectory()関数等を使うほうが安全です。
entry.ExtractToFile()関数は、極力使用を避けたほうがよいでしょう。

zipファイルフォーマット

zipファイルの末尾の構造体(セントラルディレクトリ)に、ファイル名を格納するフィールドがあります。
詳細は、参考サイト[3]を参照。
ファイル名について、含めてはならない文字列に関する規定は、今回探した限りでは見つかりませんでした。

脚注
  1. Prevent Zip Slip in .NET - Meziantou's blog ↩︎

  2. 単純に、../または..\が含まれないことを確認する対策では不十分です。ext4等のファイルシステムではシンボリックリンク、NTFSでもジャンクションがあり、対策を迂回できてしまいます。 ↩︎

  3. ZIPの仕様を日本語でまとめる · GitHub ↩︎

Discussion