tarfile en Python: crea TAR seguro

Publicado el: 17/08/2026
Tempo de leitura: 4 minutos
A laptop screen showing a code editor with visible programming code in a dimly lit environment.

El módulo tarfile en Python lee, crea y extrae archivos TAR, incluidos los comprimidos con gzip, bzip2, XZ y, en Python 3.14, Zstandard cuando está disponible. A diferencia de GZIP, que representa un solo stream, TAR es un contenedor que conserva rutas, carpetas, permisos, timestamps, enlaces y otros metadatos.

Estas funciones también generan riesgos. Un TAR malicioso puede intentar escribir fuera del destino, crear enlaces peligrosos, dispositivos especiales o agotar el disco con miles de miembros. Desde Python 3.14, el filtro de extracción predeterminado es data, más seguro que el comportamiento antiguo. Aun así, no evita todos los ataques de denegación de servicio.

Cuándo usar tarfile

Usa TAR para directorios, backups, árboles de código y artefactos Unix. ZIP suele ser más cómodo para usuarios de escritorio; consulta la guía de ZIP con Python. Para un único stream comprimido, usa gzip en Python.

Crea un TAR sin compresión

import tarfile

with tarfile.open("proyecto.tar", "x") as tar:
    tar.add("src", arcname="src")
    tar.add("README.md", arcname="README.md")

El modo exclusivo falla si el destino ya existe. arcname controla la ruta almacenada e impide filtrar directorios absolutos del sistema local.

Crea TAR comprimido

import tarfile

with tarfile.open("proyecto.tar.gz", "x:gz", compresslevel=6) as tar:
    tar.add("src", arcname="src")

with tarfile.open("datos.tar.xz", "x:xz", preset=6) as tar:
    tar.add("datos", arcname="datos")

Los modos principales incluyen w:gz, w:bz2, w:xz y w:zst. Para leer, r:* detecta la compresión. Los archivos comprimidos no admiten append normal; crea un reemplazo.

Inspecciona los miembros

import tarfile

with tarfile.open("proyecto.tar.gz", "r:*") as tar:
    for miembro in tar:
        print(miembro.name, miembro.size, miembro.type)

Cada entrada es un TarInfo. Antes de extraer, revisa nombre, tipo, tamaño, destino de enlaces y duplicados. Métodos como isfile(), isdir(), issym(), islnk() e isdev() simplifican la clasificación.

Extracción segura en Python 3.14

El filtro predeterminado ahora es data, pero declararlo explícitamente también protege código que pueda ejecutarse en versiones anteriores:

import tarfile
from pathlib import Path

origen = Path("upload.tar.gz")
destino = Path("extraccion").resolve()
destino.mkdir(parents=True, exist_ok=False)

with tarfile.open(origen, "r:*") as tar:
    tar.extractall(destino, filter="data")

El filtro rechaza rutas absolutas, rutas fuera del destino, enlaces externos y archivos especiales. También reduce permisos e ignora propietario y grupo almacenados.

El filtro data no es una sandbox

Un archivo todavía puede contener millones de miembros, archivos enormes, nombres largos, duplicados, colisiones en sistemas que ignoran mayúsculas o payloads que consumen disco y CPU. Extrae en un directorio temporal nuevo, aplica cuotas del sistema operativo y elimina el directorio completo tras un error.

Limita cantidad y tamaño

import tarfile

MAX_ARCHIVOS = 5_000
MAX_TOTAL = 2 * 1024**3

def miembros_aceptados(tar):
    total = 0
    for indice, miembro in enumerate(tar, start=1):
        if indice > MAX_ARCHIVOS:
            raise ValueError("demasiados miembros")
        if miembro.size < 0:
            raise ValueError("tamaño inválido")
        total += miembro.size
        if total > MAX_TOTAL:
            raise ValueError("el tamaño expandido superó el límite")
        if miembro.isdev() or miembro.isfifo():
            continue
        yield miembro

with tarfile.open("upload.tar", "r:*") as tar:
    tar.extractall("destino", members=miembros_aceptados(tar), filter="data")

Los tamaños del encabezado también pueden ser engañosos. Combina esta verificación con cuotas de disco y aislamiento.

Rechaza enlaces si no los necesitas

import tarfile

def solo_datos(miembro, ruta):
    miembro = tarfile.data_filter(miembro, ruta)
    if miembro is None:
        return None
    if miembro.issym() or miembro.islnk():
        return None
    return miembro

with tarfile.open("upload.tar.gz", "r:*") as tar:
    tar.extractall("destino", filter=solo_datos)

Un filtro puede devolver un TarInfo modificado, devolver None o lanzar una excepción.

Lee un miembro sin extraerlo

import tarfile
import json

with tarfile.open("paquete.tar.gz", "r:*") as tar:
    miembro = tar.getmember("manifest.json")
    if not miembro.isfile() or miembro.size > 1_000_000:
        raise ValueError("manifest inválido")
    with tar.extractfile(miembro) as archivo:
        manifesto = json.load(archivo)

Es mejor cuando solo necesitas un manifiesto, configuración o firma.

Controla los nombres al crear

tar.add() guarda por defecto la ruta suministrada. Define siempre arcname para producir una estructura portátil y no revelar directorios internos.

Filtra y normaliza metadatos

import tarfile

def preparar(info):
    if info.name.endswith((".env", ".key")):
        return None
    return info.replace(
        uid=0, gid=0, uname="root", gname="root", mtime=0
    )

with tarfile.open("fuente.tar.gz", "x:gz", compresslevel=6) as tar:
    tar.add("proyecto", arcname="proyecto", filter=preparar)

Normalizar propietario y timestamp ayuda a crear builds reproducibles. Excluye secretos, caches, entornos virtuales y archivos temporales.

Formatos de TAR

  • USTAR_FORMAT: antiguo y compatible, con límites.
  • GNU_FORMAT: extensiones para nombres y archivos grandes.
  • PAX_FORMAT: formato predeterminado, flexible y compatible con UTF-8.

PAX suele ser la mejor opción para nuevos archivos.

Modos stream

Modos como r|gz y w|gz procesan bloques secuencialmente sin acceso aleatorio. Sirven para stdin, stdout, sockets y pipes:

import sys
import tarfile

with tarfile.open(fileobj=sys.stdout.buffer, mode="w|gz", compresslevel=6) as tar:
    tar.add("resultado", arcname="resultado")

En stream, procesa cada miembro al aparecer porque no podrás volver a entradas anteriores.

Una excepción puede dejar archivos parciales

extractall() no revierte lo que ya escribió. Extrae en un directorio temporal aislado, valida el resultado y muévelo al destino definitivo solo tras el éxito.

Excepciones importantes

Maneja ReadError, CompressionError, StreamError y las subclases de FilterError. Evita errorlevel=0 en archivos externos porque los miembros rechazados pueden ignorarse y la extracción continuar.

Pruebas

Incluye rutas absolutas, ../, enlaces externos, dispositivos, FIFOs, duplicados, demasiados miembros, archivos enormes, nombres Unicode, TAR truncado y diferentes compresiones. Prueba Windows y Linux si necesitas portabilidad.

Buenas prácticas

  • Lee con r:*.
  • Crea con modo exclusivo.
  • Define arcname.
  • Extrae con filter="data".
  • Rechaza enlaces innecesarios.
  • Limita miembros, tamaño, nombres, disco y CPU.
  • Usa un destino temporal nuevo.
  • Normaliza metadatos.
  • No confíes en un TAR externo.

Conclusión

tarfile en Python empaqueta árboles de archivos y combina TAR con gzip, bzip2, XZ o Zstandard. Python 3.14 mejoró el valor predeterminado, pero las aplicaciones todavía necesitan límites explícitos y aislamiento.

Consulta la documentación oficial de tarfile y la PEP 706. Para observar extracciones grandes, revisa tracemalloc en Python y trace en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026