Security auditing de scripts: análise estática e dinâmica

1. Introdução à Segurança em Scripts Bash

Scripts Bash são amplamente utilizados para automação de tarefas administrativas, pipelines de CI/CD e operações de infraestrutura. No entanto, sua natureza interpretada e o uso intensivo de comandos externos criam uma superfície de ataque significativa. Riscos comuns incluem injeção de comandos via variáveis não sanitizadas, vazamento de variáveis de ambiente contendo senhas ou chaves, e o uso inseguro de eval que pode executar código arbitrário.

A auditoria de segurança em scripts Bash divide-se em duas abordagens complementares:
- Análise estática: examina o código-fonte sem executá-lo, identificando padrões perigosos, vulnerabilidades de sintaxe e más práticas.
- Análise dinâmica: monitora a execução real do script em ambiente controlado, observando chamadas de sistema, fluxo de dados e comportamento em tempo real.

O ciclo de vida de um script seguro envolve quatro etapas: desenvolvimento com práticas defensivas, revisão estática automatizada, testes dinâmicos em sandbox e implantação controlada em produção.

2. Análise Estática com Ferramentas de Linter e Scanner

ShellCheck: regras de segurança integradas

O ShellCheck é a ferramenta mais madura para análise estática de scripts Bash. Ele detecta automaticamente vulnerabilidades como:
- SC2069: expansão insegura de $() sem aspas
- SC2086: variáveis sem aspas duplas permitindo globbing e splitting
- SC2155: declaração de variáveis exportadas com export em vez de declare -x

Exemplo de script vulnerável e correção:

#!/bin/bash
# Script vulnerável
arquivo=$1
rm -rf $arquivo   # SC2086: variável sem aspas

# Correção segura
rm -rf "$arquivo"

Bashlint e shfmt: validação de sintaxe

O bashlint (parte do pacote checkbashisms) valida sintaxe específica do Bash, enquanto shfmt formata o código automaticamente, reduzindo ambiguidades que podem levar a vulnerabilidades.

Padrões perigosos detectáveis

# Uso inseguro de eval
eval "echo $1"   # Permite injeção de comandos

# Source de fontes externas sem validação
source "$ARQUIVO_BAIXADO"   # Código arbitrário pode ser executado

# Substituição de comando sem sanitização
resultado=$(curl -s "$URL")   # Se URL for controlada pelo atacante

3. Análise Estática Avançada com Regras Customizadas

Scripts de auditoria com grep/awk

Para cenários específicos, é possível criar auditores customizados:

#!/bin/bash
# Auditor de padrões perigosos
padroes=(
  'eval\s'
  'rm\s+-rf\s+[^"]'
  'sudo\s+.*\$'
  'exec\s+.*\$'
  'source\s+\$'
)

for padrao in "${padroes[@]}"; do
  grep -rnE "$padrao" --include='*.sh' /caminho/scripts
done

Integração com SAST (Semgrep, CodeQL)

Ferramentas de SAST como Semgrep permitem criar regras específicas para Bash:

rules:
  - id: bash-eval-injection
    pattern: eval "... $... ..."
    message: "Uso de eval com variável não sanitizada"
    severity: HIGH
    languages: [bash]

4. Análise Dinâmica por Monitoramento de Execução

Rastreamento com strace e ltrace

O strace captura chamadas de sistema, revelando operações suspeitas:

# Monitorar execução do script
strace -f -e trace=execve,open,unlink,rename ./script.sh 2>&1 | grep -E 'execve|open'

# Saída típica:
# execve("/bin/rm", ["rm", "-rf", "/tmp/dados"], ...) = 0
# open("/etc/passwd", O_RDONLY) = 3

Modo debug com bash -x

#!/bin/bash
export PS4='+ [${BASH_SOURCE}:${LINENO}] ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x

# Comandos do script
arquivo="$1"
rm -f "$arquivo"

Sandboxing com Bubblewrap

# Executar script em ambiente isolado
bwrap --ro-bind /usr /usr \
      --ro-bind /bin /bin \
      --tmpfs /tmp \
      --proc /proc \
      --dev /dev \
      --unshare-all \
      --seccomp 10 \
      ./script.sh

5. Testes de Injeção e Validação de Entrada

Simulação de ataques

# Teste de injeção via IFS
IFS=';' read -r cmd1 cmd2 <<< "comando1;rm -rf /"
echo "$cmd1"  # Apenas "comando1"

# Teste de globbing malicioso
touch '--help' '--version'
ls *  # Expande para todos os arquivos, incluindo flags

Validação defensiva

#!/bin/bash
# Validação com [[ ]]
if [[ "$1" =~ ^[a-zA-Z0-9_]+$ ]]; then
    echo "Entrada válida: $1"
else
    echo "Entrada inválida" >&2
    exit 1
fi

# Validação com case
case "$2" in
    start|stop|restart) action="$2" ;;
    *) echo "Ação inválida" >&2; exit 1 ;;
esac

Detecção com shellcheck + bash -n

# Verificação de sintaxe sem execução
bash -n script.sh || echo "Erro de sintaxe detectado"

# ShellCheck com foco em segurança
shellcheck --severity=warning --shell=bash script.sh

6. Automatização da Auditoria em Pipelines CI/CD

Integração em GitHub Actions

name: Security Audit
on: [push, pull_request]
jobs:
  shellcheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run ShellCheck
        uses: ludeeus/action-shellcheck@master
        with:
          severity: warning
          format: sarif
      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: shellcheck.sarif

Bloqueio de deploys

#!/bin/bash
# Script de auditoria para CI/CD
resultado=$(shellcheck --severity=error --format=json scripts/*.sh)
qtd_erros=$(echo "$resultado" | jq 'length')

if [ "$qtd_erros" -gt 0 ]; then
    echo "Erros críticos encontrados: $qtd_erros"
    exit 1
fi

7. Boas Práticas e Mitigações Pós-Auditoria

Substituição de comandos perigosos

# Ruim: find -exec com expansão
find /tmp -name "*.tmp" -exec rm -f {} \;

# Bom: while read com sanitização
find /tmp -name "*.tmp" -print0 | while IFS= read -r -d '' arquivo; do
    rm -f "$arquivo"
done

Configuração defensiva do shell

#!/bin/bash
set -euo pipefail
trap 'echo "Erro na linha $LINENO: comando $BASH_COMMAND"' ERR

# Tratamento seguro de arquivos temporários
tempfile=$(mktemp) || exit 1
trap 'rm -f "$tempfile"' EXIT

Documentação de exceções

# shellcheck disable=SC2086  # Intencional: expansão para múltiplos arquivos
rm -f $ARQUIVOS_TEMP

Referências