zipfile: descomprime archivos ZIP con seguridad

Actualizado el: 20/08/2026
Tempo de leitura: 4 minutos
Compactação de arquivos ZIP usando Python

Descomprimir archivos ZIP con Python es una tarea común en automatización, importación de datos, copias de seguridad y procesamiento por lotes. El módulo estándar zipfile permite listar, verificar y extraer archivos, pero una extracción segura requiere validar rutas, tamaños y contenido antes de escribir en el disco.

Para complementar esta guía, consulta los artículos sobre crear archivos ZIP, archivos en Python, manejo de excepciones, logging y automatización de tareas. Como referencias externas, revisa la documentación oficial de zipfile y la documentación oficial de shutil.unpack_archive.

Abrir y listar un ZIP

from zipfile import ZipFile

with ZipFile("documentos.zip", "r") as archivo_zip:
    for nombre in archivo_zip.namelist():
        print(nombre)

El modo r abre el archivo para lectura. namelist() devuelve los nombres internos, pero para inspeccionar tamaños y atributos es mejor utilizar infolist().

Inspeccionar entradas

with ZipFile("documentos.zip") as archivo_zip:
    for info in archivo_zip.infolist():
        print(info.filename)
        print(info.file_size)
        print(info.compress_size)
        print(info.is_dir())

file_size representa el tamaño descomprimido y compress_size el tamaño almacenado. Una diferencia extrema puede indicar una bomba de compresión.

Comprobar que el archivo es ZIP

from zipfile import is_zipfile

if not is_zipfile("documentos.zip"):
    raise ValueError("El archivo no es un ZIP válido")

La extensión .zip no garantiza que el contenido sea correcto.

Verificar integridad

with ZipFile("documentos.zip") as archivo_zip:
    entrada_danada = archivo_zip.testzip()

    if entrada_danada is not None:
        raise ValueError(f"Entrada dañada: {entrada_danada}")

testzip() comprueba el CRC de las entradas. Esta verificación detecta corrupción, aunque no sustituye otras validaciones de seguridad.

Extraer todo

from pathlib import Path
from zipfile import ZipFile

destino = Path("extraidos")
destino.mkdir(parents=True, exist_ok=True)

with ZipFile("documentos.zip") as archivo_zip:
    archivo_zip.extractall(destino)

Este patrón es adecuado para ZIP confiables. Para archivos recibidos de usuarios o de fuentes externas, valida cada entrada antes de extraer.

El riesgo Zip Slip

Una entrada maliciosa puede contener rutas como ../../archivo.txt e intentar escribir fuera de la carpeta de destino. Aunque las implementaciones modernas aplican protecciones, conviene validar explícitamente y no depender de nombres internos confiables.

from pathlib import Path


def destino_seguro(base, nombre):
    base = Path(base).resolve()
    candidato = (base / nombre).resolve()

    if base != candidato and base not in candidato.parents:
        raise ValueError(f"Ruta insegura en ZIP: {nombre}")

    return candidato

La función confirma que la ruta final permanece dentro de la carpeta autorizada.

Extraer entrada por entrada

from shutil import copyfileobj
from zipfile import ZipFile


def extraer_seguro(ruta_zip, carpeta_destino):
    carpeta_destino = Path(carpeta_destino)
    carpeta_destino.mkdir(parents=True, exist_ok=True)

    with ZipFile(ruta_zip) as archivo_zip:
        for info in archivo_zip.infolist():
            salida = destino_seguro(carpeta_destino, info.filename)

            if info.is_dir():
                salida.mkdir(parents=True, exist_ok=True)
                continue

            salida.parent.mkdir(parents=True, exist_ok=True)

            with archivo_zip.open(info) as origen, salida.open("wb") as destino:
                copyfileobj(origen, destino)

Este método ofrece control sobre cada archivo y permite aplicar límites antes de escribir.

Limitar el tamaño total

MAXIMO_TOTAL = 500 * 1024 * 1024

with ZipFile("documentos.zip") as archivo_zip:
    total = sum(info.file_size for info in archivo_zip.infolist())

    if total > MAXIMO_TOTAL:
        raise ValueError("El contenido descomprimido supera el límite")

Elige un límite compatible con el espacio en disco y el propósito de la aplicación.

Limitar cantidad de archivos

MAXIMO_ARCHIVOS = 10_000

with ZipFile("documentos.zip") as archivo_zip:
    entradas = archivo_zip.infolist()
    archivos = [info for info in entradas if not info.is_dir()]

    if len(archivos) > MAXIMO_ARCHIVOS:
        raise ValueError("El ZIP contiene demasiados archivos")

Miles de archivos pequeños pueden agotar tiempo, inodos o recursos incluso si el tamaño total es moderado.

Limitar cada entrada

MAXIMO_POR_ARCHIVO = 100 * 1024 * 1024

for info in entradas:
    if info.file_size > MAXIMO_POR_ARCHIVO:
        raise ValueError(f"Archivo demasiado grande: {info.filename}")

Combina el límite individual con el total.

Detectar una relación de compresión sospechosa

def relacion_compresion(info):
    if info.compress_size == 0:
        return float("inf") if info.file_size else 1
    return info.file_size / info.compress_size

for info in entradas:
    if relacion_compresion(info) > 200:
        raise ValueError(f"Compresión sospechosa: {info.filename}")

El umbral depende del contexto. Archivos de texto repetitivo pueden comprimirse mucho de forma legítima, por lo que esta regla debe formar parte de varias comprobaciones.

Filtrar extensiones

PERMITIDAS = {".csv", ".json", ".txt"}

for info in entradas:
    if info.is_dir():
        continue

    extension = Path(info.filename).suffix.lower()
    if extension not in PERMITIDAS:
        raise ValueError(f"Extensión no permitida: {info.filename}")

No confíes únicamente en la extensión si vas a ejecutar, interpretar o publicar el contenido. Valida también el formato real.

Evitar sobrescrituras

if salida.exists():
    raise FileExistsError(f"El destino ya existe: {salida}")

Otra estrategia es extraer en una carpeta nueva con un identificador único. Sobrescribir silenciosamente puede destruir archivos existentes.

Extraer un archivo específico

with ZipFile("documentos.zip") as archivo_zip:
    archivo_zip.extract("informes/resumen.csv", path="extraidos")

Confirma primero que el nombre exista y sea exactamente el esperado.

Leer sin extraer

with ZipFile("documentos.zip") as archivo_zip:
    with archivo_zip.open("datos/config.json") as archivo:
        contenido = archivo.read().decode("utf-8")
        print(contenido)

Esta técnica evita archivos temporales, pero establece un límite de lectura para no cargar entradas enormes en memoria.

Procesar CSV directamente

import csv
import io

with ZipFile("datos.zip") as archivo_zip:
    with archivo_zip.open("ventas.csv") as binario:
        texto = io.TextIOWrapper(binario, encoding="utf-8", newline="")
        lector = csv.DictReader(texto)

        for fila in lector:
            print(fila)

El flujo se procesa sin extraer el CSV al disco.

Archivos protegidos con contraseña

with ZipFile("protegido.zip") as archivo_zip:
    contenido = archivo_zip.read("archivo.txt", pwd=b"clave")

La compatibilidad depende del método de cifrado. No coloques contraseñas en el código y no confíes en cifrados ZIP antiguos para información sensible.

Usar shutil.unpack_archive

import shutil

shutil.unpack_archive("documentos.zip", "extraidos", format="zip")

unpack_archive() ofrece una interfaz común para varios formatos. Utilízalo con archivos confiables o después de aplicar validaciones apropiadas. zipfile proporciona más control específico.

Extraer primero en una carpeta temporal

import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as temporal:
    carpeta = Path(temporal)
    extraer_seguro("documentos.zip", carpeta)
    # Validar contenido antes de moverlo al destino final

Una carpeta temporal facilita limpiar resultados parciales si algo falla.

Mover al destino final

Después de validar nombres, tamaños y contenido, mueve los archivos aprobados. No consideres la extracción terminada hasta completar todas las verificaciones. Para procesos importantes, registra un manifiesto de lo extraído.

Registrar resultados

import logging

logging.basicConfig(
    filename="extraccion.log",
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)

logging.info("ZIP extraído: %s archivos", cantidad)

No registres contraseñas ni datos personales completos.

Manejo de errores

from zipfile import BadZipFile, LargeZipFile

try:
    extraer_seguro("documentos.zip", "extraidos")
except BadZipFile:
    print("El ZIP está dañado o no es válido")
except LargeZipFile:
    print("El archivo requiere soporte ZIP64")
except OSError as error:
    print(f"Error del sistema: {error}")

Captura excepciones específicas y no ocultes fallos con un except vacío.

Probar la función

Crea ZIP de prueba con carpetas, archivos vacíos, Unicode, rutas anidadas y entradas que intenten salir del destino. Prueba límites de tamaño, sobrescrituras y archivos corruptos. Utiliza una carpeta temporal para que las pruebas no modifiquen documentos reales.

Errores frecuentes

Los fallos habituales son llamar a extractall() sobre archivos no confiables, no comprobar espacio disponible, permitir sobrescrituras, cargar entradas gigantes en memoria, confiar en extensiones y asumir que una contraseña ZIP equivale a cifrado moderno.

Conclusión

zipfile permite listar, verificar, leer y extraer ZIP con control detallado. Para fuentes confiables, extractall() puede ser suficiente. Para archivos externos, valida que las rutas permanezcan dentro del destino, limita tamaños y cantidades, evita sobrescrituras y utiliza una carpeta temporal. La descompresión segura no consiste solo en abrir el archivo, sino en controlar todo lo que puede escribir.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Imagem ilustrativa de conteúdo Python para YouTube
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    YouTube con Python: descarga vídeos propios con yt-dlp

    Descarga copias de tus propios vídeos o contenido autorizado con Python y yt-dlp, validación, historial, límites, FFmpeg y prácticas responsables.

    Ler mais

    Tempo de leitura: 6 minutos
    12/07/2026
    Automação de postagens no Twitter usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Tweepy: automatiza publicaciones en X con Python

    Automatiza publicaciones en X con Tweepy, credenciales seguras, validación, deduplicación, programación, logs y prácticas responsables contra el spam.

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Geração e edição de planilhas Excel usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Excel con Python: crea y edita con openpyxl

    Crea y edita Excel con Pandas y openpyxl: hojas, fórmulas, estilos, filtros, validación, gráficos, rutas seguras y manejo de archivos

    Ler mais

    Tempo de leitura: 4 minutos
    12/07/2026
    Gerando aplicativo APK Android com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    APK Android con Python, Kivy y Buildozer

    Genera un APK Android con Python, Kivy y Buildozer: configuración, permisos, recursos, compilación, pruebas, almacenamiento, firma y publicación.

    Ler mais

    Tempo de leitura: 6 minutos
    12/07/2026
    Web scraper de notícias em Python com envio para Telegram
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Scraper de noticias con Python y Telegram

    Crea un scraper de noticias con Requests y Beautiful Soup, elimina duplicados y envía titulares a Telegram de forma segura

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Acesso e edição de Google Sheets com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Google Sheets: lee y edita con Python

    Accede y edita Google Sheets con Python y gspread: autenticación, lectura, escritura por lotes, validación, permisos, reintentos y seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026