この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。.NETのZipArchive/ZipArchiveEntry公式APIを確認し、ZIPを実際に展開する前に、展開先外へ出るパスや大きすぎる展開サイズを検査するpreflight checkとして整理しています。検証ステータス:📘 Microsoft公式仕様確認済み・実行未検証
ZIPを展開せず一覧表示するだけでも便利ですが、自動処理へ進めるならもう一段必要です。
「このZIPを指定フォルダへ展開してもよいか」を、展開前に判定すると、想定外パスや巨大な展開を止めやすくなります。
まず検査対象を開く
$zipPath = 'C:\Temp\sample.zip' $extractRoot = 'C:\Temp\extract' $archive = [IO.Compression.ZipFile]::OpenRead($zipPath)
ここではまだ展開しません。
1. 展開先の外へ出ないか確認する
ZIPエントリ名に .. などが含まれる場合、単純にパス結合すると意図した展開先の外を指す可能性があります。
各エントリについて「実際に展開したらどの絶対パスになるか」を計算します。
$rootFull = [IO.Path]::GetFullPath($extractRoot)
$rootPrefix = $rootFull.TrimEnd(
[IO.Path]::DirectorySeparatorChar,
[IO.Path]::AltDirectorySeparatorChar
) + [IO.Path]::DirectorySeparatorChar
foreach ($entry in $archive.Entries) {
$dest = [IO.Path]::GetFullPath(
[IO.Path]::Combine($rootFull, $entry.FullName)
)
if (-not $dest.StartsWith(
$rootPrefix,
[StringComparison]::OrdinalIgnoreCase
)) {
"BLOCK: $($entry.FullName)"
}
}
目的は、文字列として .. があるかだけを見るのではなく、正規化後の絶対パスが展開ルート配下かを確認することです。
2. 展開後サイズの合計を見る
圧縮ファイル自体は小さくても、展開後に非常に大きくなることがあります。
$totalUncompressed = (
$archive.Entries |
Measure-Object -Property Length -Sum
).Sum
"Total uncompressed: $totalUncompressed bytes"
たとえば業務上100MBまでと決めているなら、その上限を超えた時点で自動展開を止められます。
3. ファイル数にも上限を持たせる
$count = $archive.Entries.Count
if ($count -gt 1000) {
"BLOCK: too many entries ($count)"
}
合計サイズだけでなく、極端に多数のファイルが入っていないかも確認できます。
4. 想定拡張子だけ許可する
CSV受領処理なら、CSVとREADMEだけを許可する、といったルールも作れます。
$allowed = '.csv', '.txt'
foreach ($entry in $archive.Entries) {
if ([string]::IsNullOrEmpty($entry.Name)) {
continue # ディレクトリエントリ
}
$ext = [IO.Path]::GetExtension($entry.Name).ToLowerInvariant()
if ($ext -notin $allowed) {
"REVIEW: unexpected extension $ext : $($entry.FullName)"
}
}
preflight checkの流れ
flowchart TD
A[ZIPをOpenRead] --> B[パス正規化]
B --> C{展開先配下か}
C -- No --> X[BLOCK]
C -- Yes --> D[合計サイズ]
D --> E[ファイル数]
E --> F[拡張子/命名規則]
F --> G[人または後続処理が展開判断]
仕事でどう使えるか
取引先ZIPの自動受領
バッチ処理前の入力検証
バックアップ展開
CI/CD成果物の検査
ユーザーアップロードZIPの事前確認
では、「ZIPだから展開する」ではなく、展開条件を先にルール化できます。
注意点
この例だけで悪意あるZIPを完全判定できるわけではありません。
実運用ではさらに、
暗号化ZIPの扱い
シンボリックリンク等の特殊エントリ
個々のファイルサイズ上限
圧縮率
ファイル名の長さ
マルウェア検査
展開先の権限
なども用途に応じて確認します。
Disposeを必ず行う
$archive.Dispose()
実際のコードでは try/finally を使い、途中で検査エラーになっても閉じる形が安全です。
まとめ
ZIPは展開前に危険なパスを検査できる
文字列の
..検査だけでなく絶対パス正規化で判定する展開後合計サイズとファイル数に上限を持てる
許可拡張子を決めると受領処理を安全側へ寄せられる
preflight check後に初めて展開判断をする
