Arquivos temporários
Nesta aula, você aprenderá a criar e gerenciar arquivos temporários em scripts Bash com segurança, usando mktemp e trap para garantir a limpeza automática, além de conhecer boas práticas e riscos associados.
Arquivos temporários são uma necessidade comum em scripts que processam dados intermediários, armazenam resultados parciais ou precisam de um espaço de trabalho isolado. No entanto, o uso descuidado desses arquivos pode levar a problemas de segurança, sujeira no sistema e comportamentos imprevisíveis. Nesta aula, vamos explorar as ferramentas e técnicas adequadas para criar, utilizar e limpar arquivos temporários de forma robusta e segura em scripts Bash.
Dominar o gerenciamento de arquivos temporários é essencial para escrever scripts profissionais: eles devem ser únicos, ter permissões restritas e ser removidos automaticamente, mesmo em caso de erros ou interrupções. Vamos começar com o comando mktemp, a base para criação segura de temporários, e depois veremos como integrar a limpeza com trap.
mktemp
O comando mktemp é a ferramenta padrão para criar arquivos ou diretórios temporários de forma segura. Ele gera um nome único baseado em um modelo, cria o arquivo com permissões restritas (0600 para arquivos, 0700 para diretórios) e imprime o caminho completo na saída padrão. Isso evita colisões de nomes e ataques do tipo symlink, pois o arquivo é criado com a chamada de sistema O_EXCL.
Sem argumentos, mktemp cria um arquivo em /tmp com um nome aleatório. Você pode especificar um modelo com XXXXXX (pelo menos três X) que será substituído por caracteres aleatórios. Também é possível criar diretórios temporários com a opção -d, que é útil para agrupar vários arquivos temporários.
#!/bin/bash
# Criar um arquivo temporário padrão
temp_file=$(mktemp)
echo "Arquivo temporário criado: $temp_file"
# Criar um arquivo com modelo personalizado em /tmp
temp_file2=$(mktemp /tmp/meu_script.XXXXXX)
echo "Arquivo temporário com modelo: $temp_file2"
# Criar um diretório temporário
temp_dir=$(mktemp -d)
echo "Diretório temporário: $temp_dir"
# Usar o arquivo temporário
echo "Dados temporários" > "$temp_file"
cat "$temp_file"
# Limpeza manual
rm -f "$temp_file" "$temp_file2"
rm -rf "$temp_dir"
Observe que é uma boa prática usar aspas duplas ao redor das variáveis que contêm caminhos, para evitar problemas com espaços ou caracteres especiais. O mktemp também possui opções como -t para usar o diretório padrão do sistema (geralmente /tmp) e -u para gerar um nome sem criar o arquivo (não recomendado, pois há uma janela de segurança).
Limpeza com trap
A limpeza manual com rm é insuficiente em scripts que podem terminar abruptamente devido a erros, sinais ou interrupções do usuário. Para garantir que os arquivos temporários sejam removidos mesmo nesses casos, usamos o comando trap, que permite executar comandos quando o script recebe certos sinais ou quando termina. O sinal EXIT é disparado quando o script termina normalmente ou por um exit explícito, e podemos usá-lo para chamar uma função de limpeza.
Além do EXIT, é comum capturar sinais como INT (Ctrl+C) e TERM (terminação). Definimos um trap para cada sinal que queremos tratar, chamando uma função que remove os temporários. Vamos ver um exemplo completo:
#!/bin/bash
# Diretório temporário global
temp_dir=""
# Função de limpeza
cleanup() {
echo "Executando limpeza..."
if [[ -n "$temp_dir" ]]; then
rm -rf "$temp_dir"
echo "Diretório temporário removido: $temp_dir"
fi
}
# Armadilha para EXIT e sinais comuns
trap cleanup EXIT INT TERM
# Criar diretório temporário
temp_dir=$(mktemp -d)
echo "Diretório temporário criado: $temp_dir"
# Simular trabalho
sleep 2
# Se o script for interrompido, o trap será executado
echo "Script finalizado."
Nesse script, se o usuário pressionar Ctrl+C, o sinal INT é capturado, a função cleanup é chamada e o diretório é removido. O mesmo ocorre se o script terminar normalmente. É importante notar que a função de limpeza deve ser idempotente, ou seja, pode ser chamada múltiplas vezes sem causar erros (por exemplo, verificar se a variável está vazia). Também é recomendável usar trap - EXIT se você quiser desativar o trap em algum ponto.
Boas práticas
Ao trabalhar com arquivos temporários, siga estas boas práticas para escrever scripts mais seguros e confiáveis:
- Sempre use
mktempem vez de criar arquivos com nomes fixos como/tmp/meu_script.tmp. Isso evita colisões e ataques de symlink. - Prefira diretórios temporários quando precisar de vários arquivos: crie um diretório com
mktemp -de coloque todos os temporários dentro dele. Assim, a limpeza é feita com um únicorm -rf. - Defina o trap de limpeza logo no início do script, antes de criar qualquer temporário, para garantir que a limpeza seja registrada.
- Use variáveis globais ou de ambiente para armazenar os caminhos dos temporários, para que a função de limpeza possa acessá-los.
- Remova os temporários assim que não forem mais necessários, não apenas no final do script, para liberar espaço e reduzir a janela de exposição.
- Não use
mktemp -u(não criar) porque há uma corrida entre a geração do nome e a criação real, podendo ser explorada. - Considere usar
trap 'rm -f "$temp_file"' EXITpara casos simples, mas uma função é mais legível e extensível.
Exemplo de script com boas práticas:
#!/bin/bash
set -euo pipefail
# Diretório temporário global
temp_dir=""
cleanup() {
if [[ -n "$temp_dir" ]]; then
rm -rf "$temp_dir"
fi
}
trap cleanup EXIT
# Cria diretório temporário
temp_dir=$(mktemp -d)
# Trabalha dentro do diretório
cd "$temp_dir"
echo "Processando..."
# ...
# O trap limpa automaticamente na saída
Riscos
O uso inadequado de arquivos temporários pode introduzir vários riscos de segurança e robustez:
- Colisão de nomes: se você usar um nome fixo, outro processo (ou um atacante) pode criar um arquivo com o mesmo nome, levando a sobrescrita de dados ou comportamento inesperado.
- Ataques de symlink: se o arquivo temporário for criado em um diretório gravável por outros usuários (como
/tmp), um atacante pode criar um link simbólico com o nome esperado, apontando para um arquivo sensível. Quando o script escrever no temporário, pode corromper o arquivo alvo. - Permissões inadequadas: se o arquivo for criado com permissões muito permissivas, outros usuários podem ler ou modificar os dados temporários, vazando informações ou alterando o resultado do script.
- Limpeza insuficiente: se o script não remover os temporários, eles se acumulam em
/tmp, consumindo espaço e potencialmente causando problemas de disco cheio. - Janela de vulnerabilidade: quando você gera um nome e depois cria o arquivo (como em
mktemp -u), há um intervalo em que um atacante pode criar um link com esse nome, explorando a corrida.
O mktemp mitiga esses riscos ao criar o arquivo com O_EXCL e permissões restritas, e ao usar nomes aleatórios imprevisíveis. No entanto, ainda é crucial usar trap para limpeza e nunca confiar em nomes fixos.
Referências
- mktemp(1) - Linux manual page
- Bash Reference Manual: trap builtin
- Bash Special Builtins
- Bash Guide for Beginners: Traps
- ShellCheck - find bugs in your shell scripts
- OWASP: Insecure Temporary File
Exercícios
Crie um script que usa
mktemppara criar um arquivo temporário, escreva a saída do comandodatenele e, em seguida, leia e exiba o conteúdo. Faça a limpeza manualmente no final.✓ Resposta:#!/bin/bash # Cria arquivo temporário temp_file=$(mktemp) # Escreve a data no arquivo date > "$temp_file" # Exibe o conteúdo cat "$temp_file" # Limpeza manual rm -f "$temp_file"Escreva um script que crie um diretório temporário e, dentro dele, crie dois arquivos: um com o nome do usuário e outro com o nome do host. Depois, liste o conteúdo do diretório. Certifique-se de remover o diretório ao final.
✓ Resposta:#!/bin/bash # Cria diretório temporário temp_dir=$(mktemp -d) # Cria arquivos dentro user_file="$temp_dir/usuario.txt" host_file="$temp_dir/host.txt" echo "$USER" > "$user_file" echo "$HOSTNAME" > "$host_file" # Lista o conteúdo ls -l "$temp_dir" # Limpeza rm -rf "$temp_dir"Modifique o script do exercício 2 para usar
trapcom a funçãocleanupque remove o diretório temporário. Teste o script comexitem vários pontos (por exemplo, após criar o diretório, após criar os arquivos).✓ Resposta:#!/bin/bash temp_dir="" cleanup() { if [[ -n "$temp_dir" ]]; then rm -rf "$temp_dir" echo "Limpeza executada: $temp_dir" fi } trap cleanup EXIT temp_dir=$(mktemp -d) # Simula trabalho echo "Diretório criado: $temp_dir" # Ponto de saída antecipada (descomente para testar) # exit 1 echo "$USER" > "$temp_dir/usuario.txt" echo "$HOSTNAME" > "$temp_dir/host.txt" ls -l "$temp_dir" # O trap limpa automaticamente ao sairExplique por que usar
mktemp -ué perigoso e dê um exemplo de como um atacante poderia explorar essa vulnerabilidade.✓ Resposta:mktemp -ugera um nome aleatório e imprime, mas não cria o arquivo. Isso cria uma janela de tempo entre a impressão do nome e a criação real pelo script, onde um atacante pode prever o nome (ou observá-lo) e criar um link simbólico com esse nome apontando para um arquivo sensível (ex.:/etc/passwd). Quando o script tentar criar o arquivo, ele seguirá o symlink e escreverá no arquivo alvo, corrompendo-o ou permitindo que o atacante controle o conteúdo. Por isso,mktempsem-ucria o arquivo imediatamente comO_EXCL, evitando a corrida.Escreva um script que simule um processamento longo e, durante o processamento, crie vários arquivos temporários em um diretório temporário. Use
trappara limpar tudo, e teste o script enviando o sinal SIGTERM (por exemplo, comkill -TERM).✓ Resposta:#!/bin/bash temp_dir="" cleanup() { echo "Limpeza em andamento..." if [[ -n "$temp_dir" ]]; then rm -rf "$temp_dir" echo "Removido: $temp_dir" fi } trap cleanup EXIT INT TERM temp_dir=$(mktemp -d) echo "Processando... (PID $$)" # Cria vários arquivos temporários for i in {1..5}; do echo "Dados $i" > "$temp_dir/arquivo_$i.tmp" sleep 1 done # Simula trabalho longo sleep 30 echo "Concluído."Para testar, execute o script em segundo plano, anote o PID e envie
kill -TERM <PID>. O trap deve capturar o sinal e limpar o diretório.
Boas práticas e observações finais
Além das boas práticas já mencionadas, lembre-se de que o diretório padrão /tmp é limpo em algumas distribuições na reinicialização, mas não é garantido. Em sistemas críticos, considere usar /var/tmp para arquivos que precisam sobreviver a reinicializações, mas lembre-se de que ele não é limpo automaticamente. Sempre defina permissões restritas nos arquivos temporários, mesmo que o mktemp já o faça, se você modificar o arquivo posteriormente. E, finalmente, teste seus scripts com shellcheck para detectar possíveis problemas.
Com essas técnicas, você estará apto a gerenciar arquivos temporários de forma segura e eficiente em seus scripts Bash. Pratique os exercícios para consolidar o aprendizado e evite os riscos comuns discutidos.