Boas práticas de segurança em scripts
1. Tratamento de variáveis e sanitização de entrada
A sanitização de entrada é a primeira linha de defesa contra vulnerabilidades. Em scripts Bash, toda variável que recebe dados externos (argumentos, arquivos, saída de comandos) deve ser tratada como potencialmente perigosa.
Validação de parâmetros com verificações explícitas:
#!/bin/bash
set -u # Interrompe execução se variável não definida for usada
# Validação de argumentos obrigatórios
if [ $# -lt 2 ]; then
echo "Erro: Uso: $0 <arquivo> <diretório_destino>" >&2
exit 1
fi
# Validação de tipo de entrada
if [ ! -f "$1" ]; then
echo "Erro: '$1' não é um arquivo regular" >&2
exit 1
fi
# Validação de formato (exemplo: espera-se um número)
if ! [[ "$2" =~ ^[0-9]+$ ]]; then
echo "Erro: '$2' não é um número válido" >&2
exit 1
fi
Expansão segura com aspas duplas:
# Perigoso
rm -rf $user_input # Pode expandir para múltiplos argumentos
# Seguro
rm -rf "$user_input" # Trata como um único argumento
Filtragem de caracteres especiais:
# Remover caracteres perigosos de um nome de arquivo
safe_filename=$(echo "$input" | tr -d ';|&$`(){}[]<>!')
2. Controle de permissões e execução
Permissões inadequadas são responsáveis por inúmeras brechas de segurança.
Atribuição correta de permissões:
# Evite permissões 777 (leitura/escrita/execução para todos)
chmod 755 script.sh # Executável apenas pelo dono, leitura/execução para outros
chmod 700 script.sh # Apenas o dono tem acesso
Uso de umask restritivo:
#!/bin/bash
umask 077 # Arquivos criados terão permissão 600, diretórios 700
# Arquivos temporários protegidos
tempfile=$(mktemp)
echo "Dados sensíveis" > "$tempfile"
Verificação de identidade do usuário:
if [ "$(whoami)" != "root" ]; then
echo "Erro: Este script deve ser executado como root" >&2
exit 1
fi
# Verificação mais robusta com EUID
if [ "$EUID" -ne 0 ]; then
echo "Erro: Permissão negada" >&2
exit 1
fi
3. Gerenciamento seguro de senhas e segredos
Nunca armazene credenciais diretamente no código do script.
Leitura segura de senhas:
#!/bin/bash
read -s -p "Digite a senha: " password
echo
# Uso imediato sem expor nos logs
echo "$password" | mysql -u user -p database
unset password # Remove da memória após uso
Uso de variáveis de ambiente:
#!/bin/bash
# No arquivo ~/.bashrc ou ~/.profile
export DB_PASSWORD="senha_segura"
# No script
if [ -z "$DB_PASSWORD" ]; then
echo "Erro: Variável DB_PASSWORD não definida" >&2
exit 1
fi
mysql -u user -p"$DB_PASSWORD" database
Arquivo de configuração com permissões restritas:
# Criação segura
cat > /etc/meuapp/config.conf << EOF
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=senha_secreta
EOF
chmod 600 /etc/meuapp/config.conf
chown root:root /etc/meuapp/config.conf
4. Prevenção contra injeção de comandos
Injeção de comandos ocorre quando dados não sanitizados são interpretados como código.
Uso de printf com aspas:
# Perigoso
echo "Arquivo: $filename" # Expansão pode conter comandos
# Seguro
printf '%s\n' "Arquivo: $filename"
Evitar eval a todo custo:
# Extremamente perigoso
eval "comando $user_input"
# Alternativa segura
case "$user_input" in
start|stop|restart)
systemctl "$user_input" nginx
;;
*)
echo "Comando inválido" >&2
exit 1
;;
esac
Sanitização de caminhos:
#!/bin/bash
# Obter apenas o nome base do arquivo
filename=$(basename "$user_path")
# Obter caminho absoluto canônico
safe_path=$(realpath "$user_path" 2>/dev/null)
# Verificar se o caminho está dentro de um diretório permitido
if [[ "$safe_path" != /var/www/* ]]; then
echo "Erro: Acesso negado" >&2
exit 1
fi
5. Tratamento de arquivos temporários e concorrência
Arquivos temporários mal gerenciados são vetores de ataque comuns.
Criação segura com mktemp:
#!/bin/bash
# Cria arquivo temporário único e seguro
tempfile=$(mktemp /tmp/meuscript.XXXXXX)
# Cria diretório temporário
tempdir=$(mktemp -d /tmp/meuscript_dir.XXXXXX)
# Garante remoção mesmo em caso de erro
trap 'rm -rf "$tempfile" "$tempdir"' EXIT
Uso de flock para evitar corrida de processos:
#!/bin/bash
lockfile="/var/lock/meuscript.lock"
exec 200>"$lockfile"
flock -n 200 || {
echo "Erro: Script já está em execução" >&2
exit 1
}
# Região crítica do script
echo "Executando operação exclusiva..."
sleep 30
flock -u 200
Limpeza automática com trap:
#!/bin/bash
cleanup() {
local exit_code=$?
echo "Limpando recursos..."
rm -rf "$tempdir"
echo "Script finalizado com código $exit_code"
exit $exit_code
}
trap cleanup EXIT INT TERM
6. Logging seguro e auditoria
Logs mal configurados podem expor informações sensíveis.
Redirecionamento controlado de saída:
#!/bin/bash
# Redirecionar saída padrão e de erro para arquivos
exec > /var/log/meuapp.log 2>&1
# Evitar vazamento de dados sensíveis
log_info() {
local message="$1"
# Remover senhas e tokens do log
local safe_message=$(echo "$message" | sed 's/password=[^&]*/password=***/g')
echo "[INFO] $(date): $safe_message"
}
Uso de logger para syslog:
#!/bin/bash
# Log seguro via syslog
logger -t meuapp -p user.info "Operação iniciada"
logger -t meuapp -p user.err "Falha na autenticação para usuário $username"
Configuração de rotação de logs:
# /etc/logrotate.d/meuapp
/var/log/meuapp.log {
daily
rotate 7
compress
missingok
notifempty
create 640 root adm
}
7. Uso de ferramentas de verificação e hardening
Ferramentas automatizadas ajudam a identificar vulnerabilidades antes da execução.
Aplicação de shellcheck:
# Instalação
apt-get install shellcheck # Debian/Ubuntu
brew install shellcheck # macOS
# Análise do script
shellcheck -x meu_script.sh
# Exemplo de saída corretiva
# Line 6: for i in $(ls *.mp3); do
# ^-- SC2045: Iterating over ls output is fragile. Use globs.
Ativação de opções de segurança:
#!/bin/bash
set -euo pipefail
# set -e: Interrompe em qualquer erro
# set -u: Interrompe se variável não definida for usada
# set -o pipefail: Interrompe se qualquer comando no pipe falhar
# Exemplo de uso seguro
cd /diretorio/importante || {
echo "Erro: Não foi possível acessar o diretório" >&2
exit 1
}
Verificação de dependências:
#!/bin/bash
# Verificar se comandos essenciais existem
for cmd in curl jq openssl; do
if ! command -v "$cmd" &> /dev/null; then
echo "Erro: Comando '$cmd' não encontrado" >&2
exit 1
fi
done
Conclusão
A segurança em scripts Bash não é opcional — é uma responsabilidade fundamental. As práticas apresentadas neste artigo formam uma base sólida para escrever scripts robustos e seguros. Lembre-se: um script vulnerável pode comprometer todo um sistema. Invista tempo na validação de entradas, controle de permissões, gerenciamento de segredos e uso de ferramentas de análise.
Aplique estas práticas desde o início do desenvolvimento e revise scripts existentes regularmente. A segurança é um processo contínuo, não um destino.
Referências
- ShellCheck - Análise estática de scripts Shell — Ferramenta de análise estática que identifica vulnerabilidades e más práticas em scripts Bash/Shell
- OWASP Command Injection Prevention Cheat Sheet — Guia de prevenção contra injeção de comandos, com exemplos aplicáveis a scripts Shell
- Bash Reference Manual - GNU — Documentação oficial do Bash, incluindo seções sobre expansão segura e tratamento de variáveis
- Red Hat - Secure Shell Scripting Best Practices — Artigo técnico da Red Hat sobre práticas de segurança em scripts Shell
- Linux Foundation - Linux Security for Shell Scripts — 10 melhores práticas de segurança para scripts Shell, abordando permissões, variáveis e logging
- Google Shell Style Guide - Security Section — Guia de estilo do Google com seção dedicada a segurança em scripts Shell
- MITRE - Command Injection Vulnerability — Descrição detalhada da vulnerabilidade de injeção de comandos (CWE-77) com exemplos e mitigação