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