Cómo descomprimir archivos ZIP con Python de forma segura

Publicado el: 11/07/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

    Python e ícone de e-mail sobre teclado de notebook
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo automatizar correos electrónicos con Python

    Automatiza correos con Python usando EmailMessage y smtplib: texto, HTML, adjuntos, TLS, variables de entorno, reintentos y prevención de duplicados.

    Ler mais

    Tempo de leitura: 4 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

    Cómo acceder y editar Google Sheets 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
    Automação de mensagens no WhatsApp usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un bot de WhatsApp con Python de forma responsable

    Crea un bot de WhatsApp con Python usando PyWhatKit o la Cloud API oficial, con validación, seguridad, colas, pruebas y

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Bot em Python para monitorar produtos e avisar quando houver queda de preço automaticamente
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un bot de alerta de precios con Python

    Crea un bot de alerta de precios con Python, Requests y Beautiful Soup, usando Decimal, estado persistente, correo, logs y

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Extração de texto de arquivos PDF usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo extraer texto de archivos PDF con Python

    Extrae texto de PDF con Python usando pypdf y pdfplumber, procesa carpetas, tablas, archivos protegidos y detecta cuándo necesitas OCR.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Automação de tarefas com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    20 scripts de Python para automatizar tareas cotidianas

    Descubre 20 scripts de Python para organizar archivos, crear backups, procesar CSV, enviar correos, comprobar sitios y automatizar tareas seguras.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026