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

    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    gzip en Python: comprime archivos .gz

    Aprende gzip en Python para leer y escribir .gz, crear streams reproducibles, procesar datos grandes y limitar la expansión externa.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Neatly arranged blue office binders labeled with dates and names for organized storage.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    lzma en Python: comprime archivos XZ

    Aprende lzma en Python para crear archivos XZ, procesar streams, elegir checks y filtros y limitar memoria con datos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    bz2 en Python: comprime con bzip2

    Aprende bz2 en Python para comprimir archivos y bytes, procesar streams por bloques y limitar la expansión de datos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    16/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zlib en Python: comprime datos

    Aprende zlib en Python para comprimir y descomprimir bytes, procesar streams, usar checksums y limitar datos externos.

    Ler mais

    Tempo de leitura: 6 minutos
    16/08/2026
    Diagrama de archivos y sistema que representa configuración INI con configparser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    configparser en Python: archivos INI

    Aprende configparser en Python para leer y escribir archivos INI, combinar defaults, usar interpolación y actualizar de forma segura.

    Ler mais

    Tempo de leitura: 5 minutos
    16/08/2026
    Módulo de memoria RAM que representa gestión de objetos con gc en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    gc en Python: controla el recolector

    Aprende gc en Python para controlar la recolección cíclica, inspeccionar objetos, diagnosticar memoria y observar pausas.

    Ler mais

    Tempo de leitura: 7 minutos
    16/08/2026