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.







