O módulo tarfile no Python cria, lê e extrai arquivos TAR, inclusive com compressão gzip, bzip2, XZ e, no Python 3.14, Zstandard quando o suporte está disponível. Diferentemente de GZIP, que representa um único fluxo, TAR funciona como contêiner: preserva caminhos, diretórios, permissões, timestamps, links e outros metadados do sistema de arquivos.
Esse poder também cria riscos. Um TAR malicioso pode tentar escrever fora do destino, criar links perigosos, dispositivos especiais ou milhares de arquivos. Desde o Python 3.14, o filtro de extração padrão é data, mais seguro que o comportamento antigo. Mesmo assim, filtros não impedem todos os ataques de negação de serviço e não substituem uma inspeção cuidadosa.
Quando usar tarfile
Use TAR para agrupar diretórios, backups, árvores de código e artefatos Unix. Para compartilhar arquivos com usuários de desktop, ZIP pode ser mais conveniente; veja como criar ZIP com Python. Para um único fluxo GZIP, use gzip no Python.
Crie um TAR sem compressão
import tarfile
with tarfile.open("projeto.tar", "x") as tar:
tar.add("src", arcname="src")
tar.add("README.md", arcname="README.md")O modo x falha se o destino já existir, evitando sobrescrita. arcname controla o caminho armazenado e impede que diretórios absolutos locais apareçam no arquivo.
Crie TAR.GZ, TAR.BZ2 ou TAR.XZ
import tarfile
with tarfile.open("projeto.tar.gz", "x:gz", compresslevel=6) as tar:
tar.add("src", arcname="src")
with tarfile.open("dados.tar.xz", "x:xz", preset=6) as tar:
tar.add("dados", arcname="dados")Os modos principais são w:gz, w:bz2, w:xz e w:zst. Use r:* para detectar automaticamente a compressão durante a leitura. Arquivos compactados não aceitam append tradicional; crie um novo arquivo quando precisar atualizar.
Liste membros sem extrair
import tarfile
with tarfile.open("projeto.tar.gz", "r:*") as tar:
for membro in tar:
print(membro.name, membro.size, membro.type)Cada item é representado por TarInfo. Antes de extrair, examine nome, tipo, tamanho, destino de links e ocorrências duplicadas. isfile(), isdir(), issym(), islnk() e isdev() simplificam a classificação.
Extração segura no Python 3.14
O filtro padrão agora é data. Ainda assim, torne sua intenção explícita quando o código também precisa funcionar em versões anteriores:
import tarfile
from pathlib import Path
origem = Path("upload.tar.gz")
destino = Path("extracao").resolve()
destino.mkdir(parents=True, exist_ok=False)
with tarfile.open(origem, "r:*") as tar:
tar.extractall(destino, filter="data")O filtro bloqueia caminhos absolutos, caminhos que escapam do destino, links absolutos ou externos e arquivos especiais. Também reduz permissões e ignora proprietário e grupo.
O filtro data não resolve tudo
Um arquivo ainda pode conter milhões de membros, arquivos gigantes, nomes muito longos, duplicatas, colisões em sistemas sem distinção entre maiúsculas e minúsculas e conteúdo que esgota disco ou CPU. Extraia em diretório temporário novo, limite recursos no sistema operacional e remova o diretório inteiro se ocorrer falha.
Imponha quantidade e tamanho
import tarfile
MAX_ARQUIVOS = 5_000
MAX_TOTAL = 2 * 1024**3
def membros_validos(tar):
total = 0
for indice, membro in enumerate(tar, start=1):
if indice > MAX_ARQUIVOS:
raise ValueError("muitos membros")
if membro.size < 0:
raise ValueError("tamanho inválido")
total += membro.size
if total > MAX_TOTAL:
raise ValueError("arquivo expandido excedeu o limite")
if membro.isdev() or membro.isfifo():
continue
yield membro
with tarfile.open("upload.tar", "r:*") as tar:
tar.extractall("destino", members=membros_validos(tar), filter="data")O tamanho informado no cabeçalho também pode ser malicioso. Combine a verificação com cotas de disco e isolamento.
Rejeite links quando não forem necessários
Mesmo links relativos válidos aumentam a complexidade. Um filtro personalizado pode ignorá-los:
import tarfile
def somente_dados(membro, caminho):
membro = tarfile.data_filter(membro, caminho)
if membro is None:
return None
if membro.issym() or membro.islnk():
return None
return membro
with tarfile.open("upload.tar.gz", "r:*") as tar:
tar.extractall("destino", filter=somente_dados)Filtros podem devolver um TarInfo modificado, devolver None ou lançar uma exceção.
Leia um arquivo sem extrair
extractfile() devolve um leitor binário para arquivos regulares e links compatíveis:
import tarfile
import json
with tarfile.open("pacote.tar.gz", "r:*") as tar:
membro = tar.getmember("manifest.json")
if not membro.isfile() or membro.size > 1_000_000:
raise ValueError("manifesto inválido")
with tar.extractfile(membro) as arquivo:
manifesto = json.load(arquivo)Essa abordagem é melhor quando você precisa apenas de um manifesto, configuração ou assinatura.
Evite caminhos locais no arquivo
tar.add() usa o caminho fornecido como nome padrão. Sempre defina arcname para obter estrutura portátil e não vazar diretórios internos.
Filtre durante a criação
import tarfile
def preparar(info):
if info.name.endswith((".env", ".key")):
return None
info = info.replace(
uid=0, gid=0, uname="root", gname="root",
mtime=0,
)
return info
with tarfile.open("fonte.tar.gz", "x:gz", compresslevel=6) as tar:
tar.add("projeto", arcname="projeto", filter=preparar)Normalizar proprietário e timestamp ajuda a criar artefatos reproduzíveis. Confirme que segredos, caches, ambientes virtuais e arquivos temporários foram excluídos.
Formatos USTAR, GNU e PAX
USTAR_FORMAT: antigo e compatível, com limites de nome e tamanho.GNU_FORMAT: extensões para nomes longos e arquivos grandes.PAX_FORMAT: padrão atual, flexível e com metadados UTF-8.
PAX é o padrão para novos arquivos e costuma ser a melhor escolha.
Modo stream
Modos como r|gz e w|gz processam blocos sequencialmente, sem busca aleatória. Eles servem para stdin, stdout, sockets e pipes:
import sys
import tarfile
with tarfile.open(fileobj=sys.stdout.buffer, mode="w|gz", compresslevel=6) as tar:
tar.add("resultado", arcname="resultado")No modo stream, não tente voltar a membros anteriores. Planeje a ordem e processe cada item quando ele aparece.
Falhas podem deixar extração parcial
extractall() não desfaz o que já escreveu quando ocorre uma exceção. Extraia em diretório temporário exclusivo, valide o resultado e mova para o destino final somente após sucesso.
Erros importantes
Trate ReadError, CompressionError, StreamError e subclasses de FilterError. Não reduza errorlevel para zero em entradas externas, porque membros recusados podem ser apenas registrados e ignorados.
Testes essenciais
Inclua caminhos absolutos, ../, links externos, dispositivos, FIFOs, arquivos duplicados, muitos membros, arquivos enormes, nomes Unicode, TAR truncado e compressões diferentes. Teste em Windows e Linux quando portabilidade for necessária.
Boas práticas
- Use
r:*para leitura. - Use
xpara criação segura. - Defina
arcname. - Extraia com
filter="data". - Rejeite links se não forem necessários.
- Limite membros, tamanho, nomes, disco e CPU.
- Use diretório temporário novo.
- Normalize metadados ao criar.
- Não trate TAR como formato confiável.
Conclusão
O tarfile no Python é uma ferramenta completa para empacotar árvores de arquivos e combinar TAR com gzip, bzip2, XZ ou Zstandard. O Python 3.14 melhorou a segurança ao tornar data o filtro padrão, mas aplicações ainda precisam controlar links, quantidade, tamanho e isolamento.
Consulte a documentação oficial do tarfile e a PEP 706. Para investigar consumo durante grandes extrações, veja tracemalloc no Python.







