Arquivos TAR aparecem em backups, pacotes de software, pipelines de dados e rotinas de implantação. No Python, o módulo tarfile permite criar, ler e extrair arquivos .tar, .tar.gz, .tar.bz2 e .tar.xz sem instalar bibliotecas externas. Porém, extrair um arquivo recebido de terceiros exige cuidado: nomes maliciosos podem tentar gravar arquivos fora da pasta de destino, substituir arquivos existentes ou criar links inesperados. Neste guia, você aprenderá a usar os filtros de extração do tarfile, validar membros e montar uma rotina segura e reutilizável.
Por que a extração de TAR exige atenção
Um arquivo TAR não guarda apenas o conteúdo dos arquivos. Ele também pode registrar caminhos, permissões, proprietários, links simbólicos, links físicos e outros metadados. Se o programa confiar cegamente em tudo isso, um membro com nome como ../../config.py pode tentar escapar da pasta definida. Esse tipo de problema é conhecido como path traversal. Links simbólicos também podem apontar para locais externos e transformar uma extração aparentemente normal em uma sobrescrita perigosa.
A regra mais importante é simples: nunca trate um arquivo compactado externo como confiável. Mesmo quando ele vem de um usuário autenticado, de um anexo ou de um serviço conhecido, valide antes de extrair.
Conhecendo os filtros de extração
Versões modernas do Python oferecem o argumento filter em extract() e extractall(). Ele define quais metadados e tipos de membro serão aceitos. O filtro data é a escolha mais segura para arquivos de dados comuns, porque reduz características específicas do sistema e bloqueia situações arriscadas. O filtro tar mantém mais informações do formato, enquanto fully_trusted preserva o comportamento mais permissivo e deve ser reservado a arquivos totalmente controlados por você.
from pathlib import Path
import tarfile
arquivo = Path("dados.tar.gz")
destino = Path("extraidos")
destino.mkdir(parents=True, exist_ok=True)
with tarfile.open(arquivo, "r:*") as tar:
tar.extractall(destino, filter="data")
O modo r:* detecta automaticamente a compressão. O filtro data é apropriado para documentos, imagens, CSVs, JSONs e outros arquivos que não precisam preservar detalhes avançados de permissões ou links.
Inspecione os membros antes de extrair
Mesmo usando um filtro seguro, é útil observar o conteúdo do arquivo. O método getmembers() retorna objetos TarInfo com nome, tamanho, tipo e metadados. Você pode rejeitar arquivos grandes, extensões inesperadas ou quantidades excessivas de itens antes que qualquer gravação aconteça.
MAX_ARQUIVOS = 500
MAX_TAMANHO = 100 * 1024 * 1024
EXTENSOES = {".csv", ".json", ".txt", ".png", ".jpg"}
with tarfile.open("dados.tar.gz", "r:*") as tar:
membros = tar.getmembers()
if len(membros) > MAX_ARQUIVOS:
raise ValueError("Arquivo TAR contém itens demais")
total = sum(m.size for m in membros if m.isfile())
if total > MAX_TAMANHO:
raise ValueError("Conteúdo descompactado excede o limite")
for membro in membros:
if membro.isfile() and Path(membro.name).suffix.lower() not in EXTENSOES:
raise ValueError(f"Extensão não permitida: {membro.name}")
tar.extractall("extraidos", filter="data")
O limite deve considerar o tamanho descompactado, não apenas o tamanho do arquivo comprimido. Um TAR pequeno pode expandir muito e consumir disco, memória ou tempo de processamento.
Bloqueie caminhos absolutos e travessia
Quando você precisa oferecer compatibilidade com versões antigas ou aplicar regras adicionais, normalize cada caminho. Resolva a pasta de destino e o caminho final, depois confirme que o resultado permanece dentro da raiz autorizada.
def caminho_seguro(raiz: Path, nome: str) -> bool:
raiz = raiz.resolve()
candidato = (raiz / nome).resolve()
try:
candidato.relative_to(raiz)
return True
except ValueError:
return False
Não confie apenas em procurar a sequência ... Caminhos absolutos, separadores diferentes e links podem contornar verificações superficiais. O uso de resolve() e relative_to() deixa a intenção mais clara.
Evite links quando eles não são necessários
Para uploads de usuários, normalmente você quer apenas diretórios e arquivos regulares. Objetos TarInfo oferecem métodos como isfile(), isdir(), issym() e islnk(). Uma política restritiva pode rejeitar links simbólicos, links físicos, dispositivos e tipos especiais.
for membro in tar.getmembers():
if not (membro.isfile() or membro.isdir()):
raise ValueError(f"Tipo não permitido: {membro.name}")
Essa abordagem reduz a superfície de ataque e facilita prever o resultado final da extração.
Crie uma função reutilizável
from pathlib import Path
import tarfile
def extrair_tar_seguro(origem, destino, limite=200_000_000):
origem = Path(origem)
destino = Path(destino)
destino.mkdir(parents=True, exist_ok=True)
with tarfile.open(origem, "r:*") as tar:
membros = tar.getmembers()
tamanho = sum(m.size for m in membros if m.isfile())
if tamanho > limite:
raise ValueError("Limite de extração excedido")
for membro in membros:
if not caminho_seguro(destino, membro.name):
raise ValueError(f"Caminho inseguro: {membro.name}")
if not (membro.isfile() or membro.isdir()):
raise ValueError(f"Membro especial bloqueado: {membro.name}")
tar.extractall(destino, members=membros, filter="data")
Em aplicações web, execute essa função em uma pasta temporária exclusiva. O artigo sobre tempfile no Python mostra como criar diretórios temporários. Para organizar caminhos, consulte pathlib no Python. Também vale revisar o uso de with em arquivos e extração de ZIP no Python.
Tratamento de erros e limpeza
Capture erros específicos como tarfile.ReadError, tarfile.CompressionError e OSError. Se a extração falhar no meio, remova a pasta parcial. Em sistemas críticos, extraia primeiro em uma área isolada, verifique o resultado e só então mova os arquivos aprovados para o destino definitivo.
try:
extrair_tar_seguro("upload.tar.gz", "processamento")
except (tarfile.TarError, OSError, ValueError) as erro:
print(f"Falha ao extrair: {erro}")
Quando criar arquivos TAR
Para gerar um pacote, abra com w, w:gz, w:bz2 ou w:xz. Use arcname para controlar o nome interno e evitar gravar caminhos absolutos.
with tarfile.open("backup.tar.gz", "w:gz") as tar:
tar.add("relatorios", arcname="relatorios")
Para backups, combine essa técnica com o guia de backup automático em Python.
Checklist de segurança
Use filter="data", limite quantidade e tamanho, rejeite caminhos externos, bloqueie tipos especiais, extraia em pasta isolada, registre erros e mantenha o Python atualizado. Consulte a documentação oficial do tarfile e as recomendações do OWASP sobre path traversal. Com essas camadas, o módulo tarfile se torna uma solução prática para automações, importações e backups sem confiar cegamente no conteúdo recebido.







