Processing untrusted ZIP files can introduce zip bombs, excessive file creation, and directory traversal attacks.

Here are best practices to handle them securely.

Potential issues

Zip bomb

In computing, a zip bomb, also known as a decompression bomb or zip of death (ZOD), is a malicious archive file designed to crash or render useless the program or system reading it. The older the system or program, the less likely it is that the zip bomb will be detected.

A zip bomb is usually a small file for ease of transport and to avoid suspicion. However, when the file is unpacked, its contents are more than the system can handle.

Relative paths

If you decompress a zip file, but it includes the file: ../../../../../../vendor/config.exs for example. this then overwrites your config.exs file. This is a behaviour you don't want to happen.

A zillion inodes

A zip file may contain millions of 0 byte files, using up all your available inodes on the system. This would stop the hard disk being able to create new files on that partition. This could be medium-bad.

Limiting the Number of Files and Total Extracted Size

Before processing, check that the ZIP archive does not exceed reasonable limits in terms of extracted file size and file count:

def safe_zip?(file_path, max_files \\ 100, max_size \\ 10 * 1024 * 1024) do
  case :zip.list_dir(file_path) do
    {:ok, file_list} ->
      total_size = Enum.reduce(file_list, 0, fn {_name, size}, acc -> acc + size end)
      length(file_list) <= max_files && total_size <= max_size

    _ ->
      false
  end
end

if safe_zip?(file_path) do
  IO.puts("ZIP file within safe limits.")
else
  IO.puts("ZIP file exceeds safe limits!")
end

✅ Mitigates: Zip bombs and excessive file creation

Blocking directory traversal attacks

Attackers might include filenames like ../../etc/passwd inside a ZIP file. To prevent this, we reject filenames containing ../ or having the prefix /:

def valid_filenames?(file_path) do
  case :zip.list_dir(file_path) do
    {:ok, file_list} ->
      Enum.all?(file_list, fn {name, _size} ->
        not String.contains?(name, "../") && not String.starts_with?(name, "/")
      end)

    _ ->
      false
  end
end

if valid_filenames?(file_path) do
  IO.puts("No directory traversal risk detected.")
else
  IO.puts("Potential directory traversal attack detected!")
end

✅ Mitigates: Arbitrary file writes and system compromise

Enforcing file size limits in Phoenix Uploads

Before processing files in a Phoenix app, ensure upload limits are enforced at the controller level:

def upload(conn, %{
      "file" => %Plug.Upload{path: temp_path, filename: filename, content_type: mime}
    }) do
  if File.stat!(temp_path).size > 5 * 1024 * 1024 do
    conn |> put_status(:bad_request) |> json(%{error: "File too large"})
  else
    # Continue processing
  end
end

✅ Prevents: Large files from consuming excessive server resources

Conclusion

By applying these techniques, you can safely process zip files without exposing your system to common security vulnerabilities.