Segurança em shell scripts
LidoEsta aula aborda as principais vulnerabilidades de segurança em shell scripts, incluindo injeção de comandos, problemas de citação, os perigos do uso de eval e práticas de validação de entrada. O objetivo é capacitar o aluno a escrever scripts mais seguros e robustos.
Bem-vindo à aula sobre segurança em shell scripts! Nesta aula, vamos explorar os riscos de segurança mais comuns que surgem ao escrever scripts Bash e como mitigá-los. Shell scripts são frequentemente usados para automação e processamento de dados, mas se não forem escritos com cuidado, podem se tornar vetores de ataque. Vamos aprender a identificar e corrigir vulnerabilidades como injeção de comandos, citação inadequada, uso perigoso de eval e falta de validação de entrada.
A segurança em scripts não é apenas sobre proteger o sistema contra atacantes externos, mas também sobre evitar erros acidentais que podem causar danos. Ao dominar as técnicas apresentadas aqui, você será capaz de criar scripts mais confiáveis e seguros para uso em produção.
Injeção de comandos
Injeção de comandos é uma vulnerabilidade onde um atacante consegue inserir comandos arbitrários no sistema operacional através de um script. Isso ocorre quando o script executa comandos que incluem dados não confiáveis sem a devida sanitização. Por exemplo, se você usa uma variável que contém entrada do usuário diretamente em um comando, um atacante pode manipular essa entrada para executar comandos maliciosos.
Considere o seguinte script simples que lista arquivos em um diretório fornecido pelo usuário:
#!/bin/bash
echo "Digite o diretório:"
read dir
ls -la $dir
Se o usuário digitar ; rm -rf /, o comando se torna ls -la ; rm -rf /, o que pode causar danos catastróficos. Para evitar isso, devemos sempre citar as variáveis e validar a entrada. A citação é a primeira linha de defesa.
Para corrigir o exemplo, podemos usar aspas duplas em torno da variável:
#!/bin/bash
echo "Digite o diretório:"
read dir
ls -la "$dir"
Mas a citação sozinha não é suficiente se o usuário fornecer algo como /tmp; rm -rf /. Nesse caso, as aspas impedem a interpretação do ponto e vírgula, mas o argumento ainda será tratado como um único caminho, o que é mais seguro. No entanto, para maior segurança, devemos validar se o diretório existe e é realmente um diretório.
Citando sempre
Uma das regras mais importantes em shell scripting seguro é citar todas as variáveis e argumentos de comando. Sempre que você expande uma variável, use aspas duplas ou simples para evitar que o shell interprete caracteres especiais (espaços, globs, etc.) de forma indesejada. A citação dupla ("$var") preserva o valor literal, mas ainda permite expansão de variáveis. A citação simples ('$var') impede até mesmo a expansão de variáveis.
Além de prevenir injeção de comandos, a citação adequada evita erros quando os valores contêm espaços ou caracteres especiais. Por exemplo, se um arquivo se chama meu arquivo.txt, sem aspas, o shell o tratará como dois argumentos separados. Com aspas, ele será tratado como um único argumento.
Veja um exemplo de script que copia arquivos de uma lista:
#!/bin/bash
while IFS= read -r file; do
cp "$file" /backup/
done < lista.txt
Se não colocarmos aspas em "$file", arquivos com espaços no nome causarão erros. A citação também é crucial ao usar comandos como find ou rm com nomes de arquivos gerados dinamicamente.
eval (perigos)
O comando eval é extremamente perigoso em shell scripts porque ele executa o argumento como um comando do shell. Isso significa que qualquer conteúdo passado a eval é interpretado e executado, o que pode levar a injeção de comandos se o conteúdo incluir dados não confiáveis. O uso de eval deve ser evitado sempre que possível, pois ele anula todas as proteções de citação.
Um exemplo comum de uso inadequado de eval é quando se tenta construir dinamicamente um comando com variáveis:
#!/bin/bash
comando="ls -la"
eval $comando
Isso parece inofensivo, mas se $comando for construído a partir de entrada do usuário, o atacante pode injetar comandos. Por exemplo, se o usuário puder definir comando, ele pode executar qualquer coisa. Mesmo sem entrada do usuário, o uso de eval pode causar problemas com caracteres especiais.
Em vez de usar eval, prefira abordagens mais seguras, como usar arrays ou funções. Por exemplo, para executar um comando com argumentos dinâmicos, use um array:
#!/bin/bash
args=("ls" "-la")
"${args[@]}"
Isso evita a reavaliação do comando e é mais seguro. Se você precisar construir um comando complexo, considere usar bash -c com cuidado, mas ainda assim, valide e cite tudo.
Validação
A validação de entrada é essencial para garantir que os dados fornecidos ao script estão dentro dos padrões esperados. Isso reduz o risco de injeção e também previne erros lógicos. Valide sempre entradas do usuário, variáveis de ambiente, arquivos de configuração e qualquer outra fonte externa.
Existem várias técnicas de validação em Bash. Você pode usar expressões regulares com [[ =~ ]], testar tipos de arquivo, verificar intervalos numéricos, etc. Por exemplo, para validar se um argumento é um número inteiro:
#!/bin/bash
if [[ $1 =~ ^[0-9]+$ ]]; then
echo "Número válido: $1"
else
echo "Erro: argumento não é um número."
exit 1
fi
Para validar um endereço IP, você pode usar uma expressão regular mais complexa. Além disso, use case para validar opções específicas.
Outra prática é usar o comando test ou [ ] para verificar propriedades de arquivos, como existência, tipo, permissões, etc. Por exemplo:
#!/bin/bash
if [ -f "$1" ]; then
echo "$1 é um arquivo regular."
else
echo "$1 não é um arquivo regular."
fi
Valide também se um diretório é realmente um diretório antes de usá-lo em operações de escrita. Lembre-se: a validação deve ser feita antes de qualquer uso da entrada.
Boas práticas e observações finais
Além das técnicas específicas, aqui estão algumas boas práticas gerais para segurança em shell scripts:
- Sempre use
set -euo pipefailno início do script para abortar em caso de erro, evitar variáveis não definidas e detectar falhas em pipelines. - Evite usar
evalesourcecom conteúdo não confiável. - Use aspas duplas em todas as expansões de variáveis, a menos que você tenha um motivo específico para não fazê-lo.
- Prefira usar arrays para armazenar listas de argumentos, em vez de strings separadas por espaços.
- Limite o uso de comandos com privilégios elevados (como
sudo) dentro de scripts, e valide cuidadosamente os parâmetros. - Mantenha o script simples e legível, evitando construções complexas que possam esconder vulnerabilidades.
Lembre-se de que a segurança é um processo contínuo. Revise seus scripts regularmente e teste-os com entradas maliciosas para garantir que estejam protegidos.
Referências
- Bash Reference Manual
- Bash Hackers Wiki - Positional Parameters
- ShellCheck - Shell Script Analysis Tool
- OWASP - Command Injection
- nixCraft - Avoid eval command in Bash
- Red Hat - Shell Scripting Security
Exercícios
- Exercício 1: Explique por que o comando
evalé perigoso e dê um exemplo de como ele pode ser explorado. - Exercício 2: Reescreva o seguinte script para evitar injeção de comandos, citando adequadamente as variáveis:
- Exercício 3: Escreva um script que valide se um argumento é um diretório existente antes de listar seu conteúdo.
- Exercício 4: Qual é a diferença entre aspas simples e duplas em Bash? Dê um exemplo onde cada uma é apropriada.
- Exercício 5: Crie um script que receba um nome de arquivo como argumento e verifique se ele é um arquivo regular e se tem permissão de leitura. Se não, exiba uma mensagem de erro e saia com código 1.
eval executa o argumento como um comando do shell, então qualquer conteúdo não confiável passado a ele pode ser executado. Por exemplo, se uma variável contém ls; rm -rf / e você usa eval $var, o comando rm -rf / será executado. Isso permite que um atacante execute comandos arbitrários no sistema.#!/bin/bash
echo "Digite o nome do arquivo:"
read file
cat $file
#!/bin/bash
echo "Digite o nome do arquivo:"
read file
cat "$file"
A citação dupla impede que o shell interprete caracteres especiais no nome do arquivo, evitando injeção.#!/bin/bash
if [ -d "$1" ]; then
ls -la "$1"
else
echo "Erro: $1 não é um diretório válido."
exit 1
fi
'...') preservam o texto literal, sem expansão de variáveis ou interpretação de escapes. Aspas duplas ("...") permitem expansão de variáveis e interpretação de alguns escapes (como \n). Por exemplo, echo '$HOME' imprime $HOME, enquanto echo "$HOME" imprime o caminho do diretório home. Use aspas simples quando quiser uma string literal, e aspas duplas quando precisar expandir variáveis.#!/bin/bash
if [ -f "$1" ] && [ -r "$1" ]; then
echo "Arquivo válido e legível: $1"
else
echo "Erro: $1 não é um arquivo regular ou não pode ser lido."
exit 1
fi